Search code examples
javascriptarraysasp.net-mvcmodel-bindingform-post

Asp.Net MVC 4 Automatically bind model from array of objects in form post


I have built an array of objects in JavaScript and want to post them back to the server via Ajax (Im using jQuery)

The JavaScript object array looks like this:

var columns = [
    { name: 'col 1', source: 'whatever', hidden: false, width: 50 },
    ...
];

Im posting it back like this:

$.post('/MyController/MyAction', { 'columns': columns });

On the controller action Im currently getting this:

enter image description here

I have a c# object called JqColumn that I want to bind the post into, it looks like this:

public class JqGridColumn
{
    public string name;
    public string source;
    public int width;
    public bool hidden;
}

So I thought that adding a parameter in the controller action of type JqGridColumn[] columns would automatically bind the posted data, but it doesn't (it generates a array, with the correct number of elements, but each item in the array has blank values)

Can anyone tell me what Im doing wrong? Thanks!

UPDATE

At present, I am manually binding the items in my controller action as follows:

    public void ColumnChooser(JqGridColumn[] columns)
    {
        for (int i = 0; i < columns.Length; i++)
        {
            columns[i].hidden = bool.Parse(Request.Form["columns[" + i + "][hidden]"]);
            columns[i].width = int.Parse(Request.Form["columns[" + i + "][width]"]);
            columns[i].name = Request.Form["columns[" + i + "][name]"];
            columns[i].source = Request.Form["columns[" + i + "][source]"];
        }
        return;
    }

...which works fine, but I'd really like to know the .Net MVC (correct) way to do it!


Solution

  • Since you didn't register a specific ModelBinder for the JqGridColumn type, the DefaultModelBinder will be used. But:

    • It won't bind Fields, only public Properties.

    • The expected format for Array binding is columns[0].name while you're actually posting columns[0][name].

    The problem could be solved easily if you'll simply send your columns in JSON format instead of Name-Value-Pairs:

    $.ajax({
        url: '/MyController/MyAction',
        method: 'POST',
        contentType: 'application/json',
        data: JSON.stringify({ columns: columns })
    });
    

    Yet, if you don't like to change your class, you could register a ModelBinder specific for JqGridColumn and have it working even with Fields and current jQuery serialization:

    public class JqGridColumnBinder : DefaultModelBinder
    {
        protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
        {
            string name = bindingContext.ValueProvider.GetValue(bindingContext.ModelName + "[name]").AttemptedValue;
            string source = bindingContext.ValueProvider.GetValue(bindingContext.ModelName + "[source]").AttemptedValue;
            int width = (int)bindingContext.ValueProvider.GetValue(bindingContext.ModelName + "[width]").ConvertTo(typeof(int));
            bool hidden = (bool)bindingContext.ValueProvider.GetValue(bindingContext.ModelName + "[hidden]").ConvertTo(typeof(bool));
    
            return new JqGridColumn
            {
                name = name,
                source = source,
                width = width,
                hidden = hidden
            };
        }
    }
    

    Then register it in App_Start/ModelBindingConfig.cs:

    binders.Add(typeof(JqGridColumn), new JqGridColumnBinder());