192

I am creating a small app to teach myself ASP.NET MVC and JQuery, and one of the pages is a list of items in which some can be selected. Then I would like to press a button and send a List (or something equivalent) to my controller containing the ids of the items that were selected, using JQuery's Post function.

I managed to get an array with the ids of the elements that were selected, and now I want to post that. One way I could do this is to have a dummy form in my page, with a hidden value, and then set the hidden value with the selected items, and post that form; this looks crufty, though.

Is there a cleaner way to achieve this, by sending the array directly to the controller? I've tried a few different things but it looks like the controller can't map the data it's receiving. Here's the code so far:

function generateList(selectedValues) {
   var s = {
      values: selectedValues //selectedValues is an array of string
   };
   $.post("/Home/GenerateList", $.toJSON(s), function() { alert("back") }, "json");
}

And then my Controller looks like this

public ActionResult GenerateList(List<string> values)
{
    //do something
}

All I managed to get is a "null" in the controller parameter...

Any tips?

tereško
  • 58,060
  • 25
  • 98
  • 150
rodbv
  • 5,214
  • 4
  • 31
  • 31

9 Answers9

253

I modified my response to include the code for a test app I did.

Update: I have updated the jQuery to set the 'traditional' setting to true so this will work again (per @DustinDavis' answer).

First the javascript:

function test()
{
    var stringArray = new Array();
    stringArray[0] = "item1";
    stringArray[1] = "item2";
    stringArray[2] = "item3";
    var postData = { values: stringArray };
    
    $.ajax({
        type: "POST",
        url: "/Home/SaveList",
        data: postData,
        success: function(data){
            alert(data.Result);
        },
        dataType: "json",
        traditional: true
    });
}

And here's the code in my controller class:

public JsonResult SaveList(List<String> values)
{
    return Json(new { Result = String.Format("Fist item in list: '{0}'", values[0]) });
}

When I call that javascript function, I get an alert saying "First item in list: 'item1'".

Web Developer
  • 333
  • 4
  • 17
MrDustpan
  • 5,508
  • 6
  • 34
  • 39
  • 43
    There two important things to note here . 1)dataType: "json"2.) traditional: true .Without them string array wont be passed to action methods – Thanigainathan Jul 25 '11 at 07:32
  • 1
    Note that `dataType: 'JSON'` causes jQuery to attempt to parse the response as JSON, and will error if it is not valid JSON. – sennett Oct 23 '13 at 01:22
  • 8
    @Thanigainathan the `dataType: 'json'` is for the return type and is not required to send array to Action. `contentType: "application/json; charset=utf-8"`, is the one, but in some cases like this is not required. – Ruchan May 09 '14 at 06:30
  • If I have other parameters besides a List in my controller is there a way to pass them all? Say `public JsonResult Method(int id, List list, string anotherParam)` -- I can't figure out how.. – empz Feb 04 '16 at 16:13
  • 1
    @emzero use `var postData = { id: 45, [{myClassProp1: 1, myClassProp2: 2}, {}...], anotherParam: "some string" };` – Nick M Jun 04 '16 at 03:37
110

FYI: JQuery changed the way they serialize post data.

http://forum.jquery.com/topic/nested-param-serialization

You have to set the 'Traditional' setting to true, other wise

{ Values : ["1", "2", "3"] }

will come out as

Values[]=1&Values[]=2&Values[]=3

instead of

Values=1&Values=2&Values=3
Dustin Davis
  • 14,482
  • 13
  • 63
  • 119
  • 8
    This is something that got me scraching my head for a while. setting `$.ajax({ ..., traditional: true});` will help to revert to traditional serialization. – juhan_h May 31 '11 at 08:52
  • I needed this in order for an ASP.NET MVC route to properly consume a simple js array of string values. – BrandonG Mar 21 '16 at 17:19
  • Your answer is really helpful. I have same situation but it is with `ints`. When I use `traditional: true`, it works, your answer and link explains the *why* of it. It also works when I use `type: "POST"`, without using `traditional: true`. Why is that? Could you please elaborate. FYI I am using Asp.Net Mvc. – phougatv Sep 25 '16 at 07:20
  • Is there a way in ASP.NET to parse an anonymous array like this without indexes? Values[]=1&Values[]=2&Values[]=3 – Charles Owen Sep 19 '17 at 22:16
24

Thanks everyone for the answers. Another quick solution will be to use jQuery.param method with traditional parameter set to true to convert JSON object to string:

$.post("/your/url", $.param(yourJsonObject,true));
Evgenii
  • 36,389
  • 27
  • 134
  • 170
  • Works well and perfectly fitted for me as I'm using $.post() instead of $.ajax(). Thank you ! – AFract Jul 13 '16 at 15:08
10

Don't post the data as an array. To bind to a list, the key/value pairs should be submitted with the same value for each key.

You should not need a form to do this. You just need a list of key/value pairs, which you can include in the call to $.post.

Alastair Pitts
  • 19,423
  • 9
  • 68
  • 97
Craig Stuntz
  • 125,891
  • 12
  • 252
  • 273
6

In .NET4.5, MVC 5

Javascript:

object in JS: enter image description here

mechanism that does post.

    $('.button-green-large').click(function() {
        $.ajax({
            url: 'Quote',
            type: "POST",
            dataType: "json",
            data: JSON.stringify(document.selectedProduct),
            contentType: 'application/json; charset=utf-8',
        });
    });

C#

Objects:

public class WillsQuoteViewModel
{
    public string Product { get; set; }

    public List<ClaimedFee> ClaimedFees { get; set; }
}

public partial class ClaimedFee //Generated by EF6
{
    public long Id { get; set; }
    public long JourneyId { get; set; }
    public string Title { get; set; }
    public decimal Net { get; set; }
    public decimal Vat { get; set; }
    public string Type { get; set; }

    public virtual Journey Journey { get; set; }
}

Controller:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Quote(WillsQuoteViewModel data)
{
....
}

Object received:

enter image description here

Hope this saves you some time.

Matas Vaitkevicius
  • 58,075
  • 31
  • 238
  • 265
5

Another implementation that is also working with list of objects, not just strings:

JS:

var postData = {};
postData[values] = selectedValues ;

$.ajax({
    url: "/Home/SaveList",
    type: "POST",
    data: JSON.stringify(postData),
    dataType: "json",
    contentType: "application/json; charset=utf-8",
    success: function(data){
        alert(data.Result);
    }
});

Assuming that 'selectedValues' is Array of Objects.

In the controller the parameter is a list of corresponding ViewModels.

public JsonResult SaveList(List<ViewModel> values)
{    
    return Json(new { 
          Result = String.Format("Fist item in list: '{0}'", values[0].Name) 
    });
}
d.popov
  • 4,175
  • 1
  • 36
  • 47
1

As I discussed here ,

if you want to pass custom JSON object to MVC action then you can use this solution, it works like a charm.

public string GetData() {
  // InputStream contains the JSON object you've sent
  String jsonString = new StreamReader(this.Request.InputStream).ReadToEnd();

  // Deserialize it to a dictionary
  var dic =
    Newtonsoft.Json.JsonConvert.DeserializeObject < Dictionary < String,
    dynamic >> (jsonString);

  string result = "";

  result += dic["firstname"] + dic["lastname"];

  // You can even cast your object to their original type because of 'dynamic' keyword
  result += ", Age: " + (int) dic["age"];

  if ((bool) dic["married"])
    result += ", Married";

  return result;
}

The real benefit of this solution is that you don't require to define a new class for each combination of arguments and beside that, you can cast your objects to their original types easily.

You can use a helper method like this to facilitate your job:

public static Dictionary < string, dynamic > GetDic(HttpRequestBase request) {
  String jsonString = new StreamReader(request.InputStream).ReadToEnd();
  return Newtonsoft.Json.JsonConvert.DeserializeObject < Dictionary < string, dynamic >> (jsonString);
}
Mickael Lherminez
  • 679
  • 1
  • 10
  • 29
Mohsen Afshin
  • 13,273
  • 10
  • 65
  • 90
0

You can setup global parameter with

jQuery.ajaxSettings.traditional = true;
mr_squall
  • 2,215
  • 22
  • 19
-1

The answer helped me a lot in my situation so thanks for that. However for future reference people should bind to a model and then validate. This post from Phil Haack describes this for MVC 2. http://haacked.com/archive/2010/04/15/sending-json-to-an-asp-net-mvc-action-method-argument.aspx

Hope this helps someone.

DavidW
  • 49
  • 1
  • 2