For the sake of simplicity, let's say I have a User
model that has a List<Email>
as one of its properties.
public class UserModel
{
public string UserName { get; set; }
public List<Email> Emails = new List<Email>();
}
public class Email
{
public string Address { get; set; }
}
In my view, I have a list of the emails:
<table>
@foreach(Email email in Model.Emails)
{
<tr>
<td>@Html.EditorFor(modelItem => email.Address)</td>
</tr>
}
</table>
Now let's say I want the user to be able to click a button that adds a new row to the table so that the user can add a new Email to the List that is bound to their User. How do I do this? Do I need to add the new row via javascript in a certain way so that it gets bound to the model when the page is posted? I have no idea how to approach this as I'm relatively new to MVC coming from WebForms.
I would use an extension method instead that you can use in other cases as well:
Extension:
Add a property to the model, which the EditorForMany helper will store the generated index in. Without this, the Html.Validation* methods will not work (see here for a deep-dive into “why” for the curious).
Substitute @Html.EditorFor(modelItem => email.Address) with:
(Note: If you are not in a
<tr>, <tbody> or <ul>
or similar the code would be @Html.EditorForMany(x => x.Emails, x => x.Index) and you would not need @Html.EditorForManyIndexField(x => x.Emails, x => x.Index) or @Html.EditorForManyIndexField(x => x.Index). Without setting Indexfield yourself your table would be badly formatted and therefore we do it like this.)Now all of our problems are solved! You’ll see that Html.EditorForMany() uses GUIDs rather than numbers for indexes. This removes the need for us to tell our AJAX endpoint which indexes as been used; as our AJAX endpoint will instead just generate a new GUID. Html.EditorForMany() also takes care of seamlessly producing the .Index field for us as well.
All that’s left to do is to get our AJAX endpoint up and running. To do this, I define a new action in my Controller.
Create a new view Views\Shared\AddEmail.cshml;
Kudos to Matt for original article
First, your model definition needs some tweaking:
Next, what you can do is (not sure on your table implementation) display a list of the current emails, and then have a form section that can post a new email to add:
This is one of those places where MVC and WebForms dramatically diverge.
If I were doing this, I'd use AJAX to submit the new email address and return either a JSON object or the table of emails rendered as a Partial View. That way you don't have to reload the whole page. Here's and example that would return the HTML from the AJAX call, using jQuery because I'm not a fan of MVC's native AJAX functionality.
Original View:
Partial View: EmailTable
Controller Action: AddEmail
jQuery to handle the button click
Have you considered using a third party tools for this?
I have found this on CodeProject and it appears to meet your requirements. Yes it'll require a bit of tweaking, but it should do the job
http://www.codeproject.com/Articles/277576/AJAX-based-CRUD-tables-using-ASP-NET-MVC-3-and-jTa
Alternatively you can spend hours on implementing similar functionality in JavaScript/jQuery.
After some researching, I found this blog post by Steven Anderson http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/
It appears to be doing exactly what I want (except it is written in MVC2).