0

I have a view model and exists a list inside. When I send the data to controller by post, the list is comming null.

What's the problem?

View:

@model MyProject.ViewModel.MyViewModel

@{
    var grid = new WebGrid(Model.MyList);
}

@using(Html.BeginForm())
{
    @grid.GetHtml(columns: grid.Columns(grid.Column(header: "Column", format: @<text><input name="Add" type="checkbox"  @(item.Checked == true ? "Checked" : null) />@item.Name</text>)))

    <button type="submit">Send</button>
}

Controller:

[HttpPost]
public ActionResult MyMethod(MyViewModel viewModel)
{
    // In this point, my list is comming null.
    return View();
}

ViewModel:

public class ObjectModel
{
    public string Name { get; set; }
    public bool Checked { get; set; }
}

public class MyViewModel
{
    public MyViewModel()
    {
        this.MyList = new List<ObjectModel>();
    }

    public List<ObjectModel> MyList { get; set; }
}
Renato Leite
  • 761
  • 1
  • 8
  • 28
  • 1
    The problem is that the razor engine only does the data binding for html helpers, so since you are using a WebGrid it won't do it automatically. You might want to look into this similar question: http://stackoverflow.com/questions/15113839/post-items-of-webgrid-asp-net-mvc3 – SOfanatic Feb 06 '14 at 19:52

1 Answers1

0

Your problem is that after submitting the default model binder is not able to properly bind your data. To be able to do so your input elements would have to contain appropriate syntax inside its name attribute, sth like:

<input name="MyList[0].Name"
<input name="MyList[1].Name"
//and so on

There are a in fact a few solutions to your challenge. Personally I would create my own custom binder and then iterate through the FormCollection. To be able to distinquish between different items you would have to change this part:

<input name="Add"

into:

<input name="Add" value="@item.Name"

Then your custom binder could look like this:

public class MyViewModelBinder: IModelBinder
{
    public object BindModel(ControllerContext controllerContext, 
                            ModelBindingContext bindingContext)
    {
        HttpRequestBase request = controllerContext.HttpContext.Request;

        List<ObjectModel> list = new List<ObjectModel>(); 
        var ids= form.GetValues("Add"); 
        //in your case your ids would be the names of your items
        foreach (var id in ids)
        {
           list.Add(new ObjectModel(){ Name = id, Checked= true});  
        }

        return new MyViewModel
                   {
                       MyList = list
                   };
    }
} 

Finally you should add this to your global collection of model binders:

protected void Application_Start()
{
    // some configuration code
    ModelBinders.Binders.Add(typeof(MyViewModel), new MyViewModelBinder());
}

You could either set it as a default model binder for your MyViewModel or simply add this to MyMethod action:

[HttpPost]
public ActionResult MyMethod
  ([ModelBinder(typeof(MyViewModelBinder))]MyViewModel viewModel)
{
    // In this point, your list will not come null
    return View();
}
Paweł Bejger
  • 6,176
  • 21
  • 26