0

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!

sTx
  • 1,213
  • 13
  • 32

2 Answers2

0

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.

Brian Mains
  • 50,520
  • 35
  • 148
  • 257
  • I have an array of $("#x").serialize(), I get the idea but it think same thing will happen – sTx Dec 14 '20 at 13:05
  • You have more than one form you are trying to post back to the server? – Brian Mains Dec 14 '20 at 13:56
  • kind of, I have one form, but when I submit, data is appended to a preview div, and in same time is saved serialized in a javascript array variabile . On each submit I push data in that variable. Finally I send this array variable to server through ajax -> here I have the problem(when data gets to server is null) – sTx Dec 14 '20 at 14:05
  • basically I have a modal with a form where I input some customer info(let's say: name, surname, email, phone) -> when submit, this customer is printed to a preview section in my modal. After I add 3,4 customers to this preview section I send an array with customers to server(the actual submit to server). – sTx Dec 14 '20 at 14:08
0

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) { ... }
sTx
  • 1,213
  • 13
  • 32