Try adding a hidden input for the list item, using the name of the property (in your case "Names"), and the item's index in square brackets. This definitely works:
<div id="names">
<ul>
<% for (int i = 0; i < Model.Names.Count; i++ ) { string name = Model.Names.ElementAt(i); %>
<li>
<%: Html.Hidden("Names[" + i + "]", name) %><%: name %><img src="/delete.png"/>
</li>
<% } %>
</ul>
</div>
The model binder needs some sort of context to decide how to bind complex types. By providing a hidden element with the correct name, it has enough context to decide that the item belongs in the Names property at index i. If you don't include the index, it uses a finite set of items (meaning you can't add or remove items from the list, and calling .Add()
or .Remove()
will throw an exception).
While I agree that the jQuery method will work, if no other part of your page is using JSON then why do a one off? The name on the hidden element is doing exactly the same thing, providing a name/value pair with its index. Only this way, the model binder does the work for you (for a small price of less-than-gorgeous view syntax). On the plus side, no javascript necessary to get this model binding properly.
If you're using IEnumerable<string>
or ICollection<string>
, you have to use .ElementAt(i)
like the above sample. If you're using an IList<string>
or string[]
, you can use an indexer instead.
EDIT: Solving the deletion problem
Create another view model for your strings, so you'll have a IEnumerable<NameViewModel> Names { get; set; }
property on your actual view model (in place of IEnumerable<string>
). NameViewModel will look like this:
public class NameViewModel
{
public string Name { get; set; }
public bool IsDeleted { get; set; }
}
Then, add a hidden element for IsDeleted
to the <li>
. Remember that the model binder requires the proper name for context. After adding a property to the indexed name, the hidden inputs will look like this:
<%: Html.Hidden("Names[" + i + "].Name", name.Name) %>
<%: Html.Hidden("Names[" + i + "].IsDeleted", name.IsDeleted) %>
In your jQuery delete code, find the hidden element and set the value to True
, and call .hide()
on the list item. When you post the form to the server, your action method can figure out what should be deleted like this:
var names = model.Names.Where(name => name.IsDeleted).Select(t => t.Name);