mitomed
mitomed

Reputation: 2066

Pass a dictionary as a parameter in Http.Post in C#

I'm trying to pass a dictionary to an action method inside an Html.Post

Html.BeginForm("ProcessStage2", "Supplier", new {bookVars = Model.BookVars} , FormMethod.Post, new { name = "frmBook" })

I can pass other properties (int, string, ...) of the model but not the Dictionary. Is there any chance to achieve that? because the dictionary has 29 pairs, which seem to be too many to split and pass them separated.

Upvotes: 3

Views: 7594

Answers (4)

jwaliszko
jwaliszko

Reputation: 17064

You can do that using for example json serialization. I recommend this because it is very flexible and looks elegantly.

Your model can contain any set of objects. In your case it's:

public class MyViewModel
{
    public IDictionary<string, string> BookVars { get; set; }
}

In the controller you can stub some data for our needs:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        // mock data
        var model = new MyViewModel
                            {BookVars = new Dictionary<string, string> {{"key1", "value1"}, {"key2", "value2"}}};

        return View(model);
    }

    [HttpPost]
    public ActionResult Index(MyViewModel model)
    {
        // the binded model is avaliable
        return View(model);
    }
}

The view contains a form, and includes a custom script for ajax submission. The values can be persisted and modified in any way. You can use for example hidden fields.

@model MyViewModel

<script src="/Scripts/script.js" type="text/javascript"></script>

<script type="text/javascript">
        $(document).ready(function () {
            $('form').frmBookSubmit();
        });
</script>

@using (Html.BeginForm("Index", "Home", FormMethod.Post, new { name = "frmBook" }))
{
    <div class="books">
        @foreach (var item in Model.BookVars)
        {
            <input type="hidden" key="@item.Key" value="@item.Value" />        
        }
    </div>
    <input type="submit" />
}

Script.js, creates a simple jquery plugin for ajax submission using json serialization of your data:

(function ($) {

    $.fn.frmBookSubmit = function () {
        var $this = $(this);
        if ($this != null && $this != 'undefined' && $this.length > 0) {
            $this.submit(function (e) {
                e.preventDefault();

                var myviewmodel = new Object();
                myviewmodel.BookVars = $this.find(".books").booksCollect();
                var data = { model: myviewmodel };
                var jsonString = JSON.stringify(data);

                $.ajax({
                    url: $this.attr("action"),
                    type: 'POST',
                    dataType: 'json',
                    data: jsonString,
                    contentType: 'application/json; charset=utf-8'
                });

            });
        }
    };

    $.fn.booksCollect = function () {
        var books = new Array();
        $(this).find("input").each(function () {
            var k = $(this).attr('key');
            var v = $(this).attr('value');
            var item = {
                Key: k,
                Value: v
            };
            books.push(item);
        });
        return books;
    };

})(jQuery);

If you find it useful, you can also write your custom json binder using for example Newtonsoft.Json library.

You're done.

Upvotes: 0

Darin Dimitrov
Darin Dimitrov

Reputation: 1038720

You could use hidden fields and editor templates. Let's take an example:

Model:

public class MyViewModel
{
    public IDictionary<string, string> BookVars { get; set; }
}

Controller:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var model = new MyViewModel
        {
            // fill the dictionary with some dummy data
            BookVars = Enumerable
                .Range(1, 5)
                .ToDictionary(x => "key" + x, x => "value" + x)
        };
        return View(model);
    }

    [HttpPost]
    public ActionResult Index(MyViewModel model)
    {
        // the model.BookVars property will be properly bound here
        return View(model);
    }
}

View (~/Views/Home/Index.cshtml):

@model MyViewModel

@using (Html.BeginForm())
{
    @Html.EditorFor(x => x.BookVars)
    <button type="submit">OK</button>
}

Editor template (~/Views/Home/EditorTemplates/KeyValuePair`2.cshtml):

@model KeyValuePair<string, string>

@Html.Hidden("Key", Model.Key)
@Html.Hidden("Value", Model.Value)

Notice the name of the editor template which will automatically be rendered for each element of the dictionary: KeyValuePair`2.cshtml

Sorry I cannot use SO's editor to properly format this but the name of the file should be: KeyValuePair[grave accent]2.cshtml where [grave accent] is the grave accent character.

Also don't forget to read about the wire format for collections and dictionaries that the default model binder expects to better understand what's happening under the covers.

Upvotes: 3

Nicolas Repiquet
Nicolas Repiquet

Reputation: 9255

Serialize your dictionary and pass it as a string (possibly base64 encoded).

Upvotes: 3

Kamil Będkowski
Kamil Będkowski

Reputation: 1092

No,You can't pass object as parameter. Only way is store object in database and pass in POST only id for that object. You should create model containing that Dictionary:

public int id {get;set;}
public Dictionary<string, int> Dictionary {get;set};

and load it by id from server side.

Upvotes: 2

Related Questions