We are trying to send multiple forms with one Ajax (jQuery) Call to an ASP application.
We use the following jQuery code:
var formContainer = {
Form1 : form1.serialize(),
Form2 : form2.serialize()
}
$.ajax({
type: "POST",
url: '@Url.Action("CreateModel", "Controller")',
data: formContainer,
success: function (result) { }
});
On the server we receive the following in the Request.Form property:
Key : Value
Form1 : All serialized form elements for Form1
Form2 : All serialized form elements for Form2
Normally we use the following method so ASP is automaticly creating the object with the right property value:
public ActionResult CreateModel(ClassForForm1 obj)
But because the two forms are send together the modelbinder cannot bind and build the class. So for this action we want the modelbuilder to use the values in Request.Form["Form1"].
We can't use a custom modelbinder, because we use an extern library (DevExpress ,they wrote an own implementation above this).
We are using the MEF framework to add functionalities (these functionalities are added as forms on the view). For this reason we do not know what too expect on the backend. So writing a wrapper ViewModel is not acceptable.
The functionality for proccessing the other forms data will be handeled inside other modules.
Any solutions are welcome!
Thanks in advance.
This is typically done using a combined view model. Otherwise, you would need to manually parse the request parameters. Here is a fiddle showing how to combine the data from multiple forms.
$(function() {
$('button').click(function(e) {
var form1 = $('#form1');
var form2 = $('#form2');
$.ajax({
type: "POST",
url: '/echo/html/',
data: form1.serialize()+"&"+form2.serialize(),
success: function (result) {
alert(result);
}
});
});
});
On the server, your view model would require:
public class IndexViewModel {
// properties from form1
public string first { get; set; }
// properties from form2
public string last { get; set; }
}
public class First {
public string first { get; set; }
}
public class Last {
public string last { get; set; }
}
And your action signature:
[HttpPost]
public ActionResult Index(IndexViewModel model) {
var firstModel = (new First()).CloneMatching(model);
var lastModel = (new Last()).CloneMatching(model);
return RedirectToAction("Thanks");
}
See Best way to clone properties of disparate objects for the CloneMatching extension method.
If you create you javascript object like this:
var formContainer = { obj : {
Form1 : form1.serialize(),
Form2 : form2.serialize()
}
}
The controller should match it up with the name 'obj' you created in the javascript with the 'obj' in your method....
public ActionResult CreateModel(ClassForForm1 obj)
My previous sample worked just because my class has name and value props. I'm realy sorry for that. But now you can see working DEMO
JS
function mapForm(form)
{
var result = {};
$.map($(form).serializeArray(),
function(el){
mapFormProperty(result, el);
});
return result;
}
function mapFormProperty(form, property)
{
form[property.name] = property.value;
}
$('.submit').click(function(){
var form1 = mapForm($('#form1'));
var form2 = mapForm($('#form2'));
var formContainer = {
'Form1': form1,
'Form2': form2};
$.ajax({
type: "POST",
url: '@Url.Action("CreateModel", "Controller")',
data: JSON.stringify(formContainer),
success: function (result) { }
});
Operations with forms and form container should give you next json string
"{"Form1":{"Prop1":"Value1","Prop2":"Value2"},"Form2":{"Prop1":"Value1","Prop2":"Value2"}}"
And your model binder will be able solve this, if you change your action signature
Action
public ActionResult CreateModel(ClassForForm1 Form1) //argument name must be equal to data property
//but not equal to class name
And it should work. it works in my test sample
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With