Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Posting/Binding multiple forms on backend ASP.NET MVC (MEF)

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.

like image 804
Stefan Koenen Avatar asked Jul 11 '14 13:07

Stefan Koenen


3 Answers

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.

like image 197
B2K Avatar answered Nov 01 '22 13:11

B2K


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)
like image 39
Jim Crandall Avatar answered Nov 01 '22 12:11

Jim Crandall


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

like image 1
Baximilian Avatar answered Nov 01 '22 12:11

Baximilian