8

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.

Dave Alperovich
  • 32,320
  • 8
  • 79
  • 101
Stefan Koenen
  • 2,289
  • 2
  • 20
  • 32
  • When you say "So for this action we want the modelbuilder to use the values in Request.Form["Form1"]." you essentially want Form2 values to be ignored for that request? Or is it that you want to call CreateModel for **each form** in the request, such that if 3 forms are sent, CreateModel is performed three times, once for each form? – AaronLS Jul 15 '14 at 20:24
  • Yes, we want to "ignore" the values in Request.Form["Form2"] like you said. – Stefan Koenen Jul 15 '14 at 21:49
  • I tryed to change the Request.Form values in a custom modelbinder, but it's reading values from an IUnvalidatedprovider. Though it was the last code(in the static function) on this page: http://blogs.taiga.nl/martijn/2011/09/29/custom-model-binders-and-request-validation/ . But i couldn't find how to change the values in that one. This is what i found myself, other solutions are welcome ofcourse :). – Stefan Koenen Jul 15 '14 at 21:57

3 Answers3

2

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.

Community
  • 1
  • 1
B2K
  • 2,541
  • 1
  • 22
  • 34
  • You could also use composition on the view model class, and `@Html.EditorFor(model => model.Form1.first)` for example. – B2K Jul 14 '14 at 20:54
1

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)
1

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

Baximilian
  • 885
  • 7
  • 25
  • Can you upload your sample project maybe? When i call the serializeArray function on my form i get an array of objects, and in your code above you give multiple properties per form ( prop1 & prop2) – Stefan Koenen Jul 15 '14 at 22:11