skip to Main Content

As I mentioned in title I’m trying to send an array of view models back to a controller through an Ajax request.

I’ll use some dummy values and names:

MyViewModel.cs

int VM_id1;
int VM_id2;
string VM_str1;
string VM_str2;

Controller

// here I get 0 and null values
public ActionResult MyPostController(List<MyViewModel> vm) { ... }

View with ajax

In my view I have a form with fields from above, and a preview window where every time I submit form, those values are added there(not posting to controller yet). Then, when I’m ok with the list of added values I have another submit button that sends the array of view models to controller.

var vmData = new Array();
...
vmData.push($("form").serializeArray());//not the exact code but the idea is that I'm pushing serialized data from form
// the ajax request
$.ajax({
   type:"POST" ,
   url: "someURL"
   dataType: "json"   ,
   contentType: "application/json; charset=utf-8",
   data: JSON.stringify(vmData)
   //data: {vm: JSON.stringify(vmData)}//this generates an error
})

vmData in Chrome console looks like this (example for 2 array values):

(2)[Array(4), Array(4)]
[
    0 : Array(4)
       0:{name: "VM_id1" ,value: "123"}
       1:{name: "VM_id2" ,value: "99"}
       2:{name: "VM_str1" ,value: "string1"}
       3:{name: "VM_str2" ,value: "string2"}
    1 : Array(4)
       0:{name: "VM_id1" ,value: "1"}
       1:{name: "VM_id2" ,value: "55"}
       2:{name: "VM_str1" ,value: "someval1"}
       3:{name: "VM_str2" ,value: "someval1"}
 ]

vmData stringified:

[
  0: [{name: "VM_id1" ,value: "123"}, 1:{name: "VM_id2" ,value: "99"},{name: "VM_str1" ,value: "string1"} ,{name: "VM_str2" ,value: "string2"}]
  1:[{...},{...},{...},{...}]
]

Problem: when data is posted in controller I get 2 list items with null values for all fields. What am I doing wrong?

Another thing, if I manage to solve this, is that I want to pass __RequestVerificationToken also, but that’s another problem.

Thank you for your time!

2

Answers


  1. Chosen as BEST ANSWER

    So, main problem was that server expected other format of data -> so a flattening of the serializeArray() output was required.

    var formData = $("form").serializeArray();
    
    // flattening
    var toSend = {};
    formData .forEach(function(x){ toSend[x.name] = x.value; })
    
    // get __RequestVerificationToken value
    headers["__RequestVerificationToken"] = formData.reverse(0.pop().value;
    
    // pushing to array of viewModels
    vmData.push(toSend);
    

    So this:

     [
           0:{name: "VM_id1" ,value: "123"}
           1:{name: "VM_id2" ,value: "99"}
           2:{name: "VM_str1" ,value: "string1"}
           3:{name: "VM_str2" ,value: "string2"}
     ]
    

    Gets flattened to this(which is what server expected):

    {
      VM_id1 : "123",
      VM_id2 : "99",
      VM_str1 : "string1",
      VM_str2 : "string2"
    }
    

    Also I needed to send __RequestVerificationToken to controller as well -> solved with this approach.

    Finally my ajax looks like this:

      $.ajax({
           type:"POST" ,
           url: "someURL"
           dataType: "json"   ,
           contentType: "application/json; charset=utf-8",
           headers: headers,
           data: JSON.stringify(vmData)
        })
    

    And my controller action

    [HttpPost]
    [ValidateJsonAntiforgeryToken]
    public ActionResult MyPostController(IEnumerable<MyViewModel> vmData) { ... }
    

  2. I do what you are doing, but a slightly different way. So I create a class wrapper like so:

    public class HomeIndexModel
    {
    
      public List<MyViewModel> Items { get; set; }
    
    }
    

    In the form I’ll have:

    <form id="x" ..>
    
       @for (var i = 0; i < Model.Items.Count; i++)
       {
          ..
          @Html.TextBoxFor(m => m.Items[i].Name)
          ..
       }
    
    </form>
    

    And I’ll do the following to send it back to the server:

    $.ajax({
       type: "post",
       url: "..",
       data: $("#x").serialize()
    });
    

    And that can be received by this action:

    public ActionResult Index(HomeIndexModel model)
    {
       var items = model.Items.Where(i => i.IsSelected == true);
    
       //Save
    }
    

    Your way could work here too; I find having the top-level wrapper has some benefits (it makes it easier to pass along additional data). To include a serialization token, a serialization token is essentially a form element that gets rendered, and as such, it should be included in the form serialization and sent back to the server.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search