Model Binding to a List MVC 4

How does MVC 4 List Model Binding work?

There is a specific wire format for use with collections. This is discussed on Scott Hanselman's blog here:

http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx

Another blog entry from Phil Haack talks about this here:

http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx

Finally, a blog entry that does exactly what you want here:

http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/

Model Binding to a List MVC 4

This is how I do it if I need a form displayed for each item, and inputs for various properties. Really depends on what I'm trying to do though.

ViewModel looks like this:

public class MyViewModel
{
public List<Person> Persons{get;set;}
}

View(with BeginForm of course):

@model MyViewModel


@for( int i = 0; i < Model.Persons.Count(); ++i)
{
@Html.HiddenFor(m => m.Persons[i].PersonId)
@Html.EditorFor(m => m.Persons[i].FirstName)
@Html.EditorFor(m => m.Persons[i].LastName)
}

Action:

[HttpPost]public ViewResult(MyViewModel vm)
{
...

Note that on post back only properties which had inputs available will have values. I.e., if Person had a .SSN property, it would not be available in the post action because it wasn't a field in the form.

Note that the way MVC's model binding works, it will only look for consecutive ID's. So doing something like this where you conditionally hide an item will cause it to not bind any data after the 5th item, because once it encounters a gap in the IDs, it will stop binding. Even if there were 10 people, you would only get the first 4 on the postback:

@for( int i = 0; i < Model.Persons.Count(); ++i)
{
if(i != 4)//conditionally hide 5th item,
{ //but BUG occurs on postback, all items after 5th will not be bound to the the list
@Html.HiddenFor(m => m.Persons[i].PersonId)
@Html.EditorFor(m => m.Persons[i].FirstName)
@Html.EditorFor(m => m.Persons[i].LastName)
}
}

MVC4 - Partial View List Model binding during Submit to Main view

Interesting Question :)

The issue was that Model binding to list should have unique names. So the Generated HTML Should look like below:

<input id="Quotations_0__PRDocumentId" name="Quotations[0].PRDocumentId" type="hidden" value="0">

<input id="Quotations_1__PRDocumentId" name="Quotations[1].PRDocumentId" type="hidden" value="0">

The recommended solution is to use Editor Templates, Check this and this.

But I am giving alternate solution below using for loop to create unique names with index, taken from this post which faced same issue.

In Main View:

Pass The Main Model Instead

@Html.Partial("_PRDocs", Model)

Partial View:

@model JKLLPOApprovalApp.Models.PRDocument

<table class="table">
@if (Model.Quotations != null)
{
for (var i = 0; i < Model.Quotations.Count(); i++)
{
<tr>
<th>
@Html.DisplayNameFor(model => Model.Quotations[i].FileName)
</th>

<th></th>
</tr>

<tr>
@Html.HiddenFor(modelItem => Model.Quotations[i].PRDocumentId)
<td>
@Html.DisplayFor(modelItem => Model.Quotations[i].FileName)
</td>
<td>
@Html.ActionLink("Delete", "Delete", new { id = Model.Quotations[i].Id })
</td>
</tr>
}
}

</table>

Hope helps.

C# ASP.NET MVC Model Binding for List T

since you are submitting a form , the input parameter of the action should be the same as the form model

public IActionResult Form_AddOrderItems(FormAddOrderItemModel model) 

and your view model can be simplier

@model Restaurant.Models.Order.Form.FormAddOrderItemModel

ASP.NET MVC 4 Model binding issue

Your Fiddler request shows what is emitted from the browser. It isn't sending your ExcludeClientsWithoutAddress property.

Since this property is not marked nullable bool? it is being assigned a default value in binding.

You have these inputs as ng_model which suggests your Angular code is not sending this field.

MVC 4 binding to List for child table

You need to return a MultiSelectList from your generic list.

Here is an example of how to do it:

var appliedTaxes = new List<TaxApplied>();
appliedTaxes.Add(new TaxApplied { ID = 1, ProductID = 1 });
var items = appliedTaxes.Select(t => new SelectListItem { Text = t.ID.ToString(), Value = t.ProductID.ToString() }).ToList();
ViewBag.AppliedTaxes = new MultiSelectList(items, "Text", "Value");

Please note I am only using the Id's as the text and value for demonstration purposes.

Update

Change your post to this:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Product product)
{
ViewBag.NavProduct = "active";
if (ModelState.IsValid)
{
db.Products.Add(product);
db.SaveChanges();
return RedirectToAction("Index");
}

MultiSelectList taxes = new MultiSelectList(db.Taxes.ToList<Tax>(), "ID", "Name");
ViewBag.AppliedTaxes = taxes;

return View()
}

ASP.Net MVC4 bind a create view to a model that contains List

I recently found myself needing to accomplish the same task and, like you, not wanting to add a bunch of javascript. I'm using MVC4 and, as best I can tell, there doesn't appear to be an out-of-the-box way to bind enumerable properties of a model to a view. :(

However, as you demonstrated in your question, it is possible to retrieve enumerable properties from the model in a view. The trick is just getting the updates back to the controller. Going off of your example models, your view could look like this (you don't need to make a partial):

@model MVCComplexObjects.Models.ComplexObject

<p>
@Html.ActionLink("Create New", "Create")
</p>

@using (Html.BeginForm("SaveNew", "Home", FormMethod.Post))
{
<table>

<tr>
<th>
@Html.DisplayNameFor(model => model.contents[0].name)
</th>
<th>
@Html.DisplayNameFor(model => model.contents[0].data)
</th>
<th></th>
</tr>

@for (int i = 0; i < Model.contents.Count; i++)
{
<tr>
<td>
@Html.TextBox("updatedContents["+i+"].name", Model.contents[i].name)
</td>
<td>
@Html.TextBox("updatedContents["+i+"].data", Model.contents[i].data)
</td>
<td>
@* Got rid of the edit and detail links here because this form can now act as both *@
@Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
</td>
</tr>
}

</table>

<input type="submit" value="Save" />
}

And your controller action would look like this:

[HttpPost]
public ActionResult SaveNew(ICollection<ContainedObject> updatedContents)
{
foreach (var co in updatedContents)
{
//Update the contained object...
}

return RedirectToAction("Index");
}

Basically, we are defining a new collection object in the view for MVC to pass to your action method upon form submission. The new object ("updatedContents" in this example) is basically the same as the list property ("contents", in this example) that was defined and populated in the ComplexObject model.

This is a bit more work, but does accomplish the goal of not needing any javascript for the post back. Everything can be done with standard MVC.

How to bind Model from get to post method MVC 4

These values are null, because they are not present in your view. You will need to keep them in your View in the form of a hidden control. The ViewModel that you receive in the post can only construct the ViewModel using values present in the View. Since there is no ID maintained in the View, the constructed ViewModel has a null ID.

You can use

@Html.HiddenFor(model => model.Questions.ID) 

and for your answer ID

@Html.HiddenFor(m => m.Answers[i].ID)


Related Topics



Leave a reply



Submit