Click here to Skip to main content
Click here to Skip to main content

Tagged as

List Model Binding in MVC

, 6 May 2014
Rate this:
Please Sign up or sign in to vote.
ASP.NET MVC List Model binding

Introduction

I am new to MVC as I just started working on a project using it. I was stuck on this model binding thingy for a couple of days, so just want to share this experience. Hope it will be useful to other newbies like me.

Background

I have this master-detail (one to many) relationship in one view and the detail is represented by HTML table in which user can add/remove the rows as they like. And I want the Add method in the controller to just accept the model as a parameter. Something like this:

I found this great article here which helped me a lot.

Using the Code

OK, just to make it simple, I will create 2 classes with master-detail relationship in the model class.

I name it AuthorModel:

 public class Author
    {
        public Author()
        {
            Books = new List<Book>();
        }
        public string Name { get; set; }
        public List<Book> Books { get; set; }
    }

    public class Book
    {
        public string Title { get; set; }
        public DateTime PublishedDate { get; set; }
    } 

Next, we add controller and view into the project:

Controller :

public class AuthorController : Controller
    {
        //
        // GET: /Author/

        public ActionResult Index()
        {
            var model = new Author();
            return View(model);
        }
      
    } 

View :

@using MvcModelBinding.Models
@model MvcModelBinding.Models.Author
@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>
    Index</h2>
@using (Html.BeginForm())
{
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>Author</legend>
        <div class="editor-label">
            @Html.LabelFor(model => model.Name)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Name)
            @Html.ValidationMessageFor(model => model.Name)
        </div>
        <div>
            <input id="btnAddBook" type="button" 
            onclick="addRow();" value="Add Book" />
        </div>
        <table>
            <thead>
                <tr>
                    <td>
                        Title
                    </td>
                    <td>
                        Published Date
                    </td>
                </tr>
            </thead>
            <tbody id="tbBooks">
            </tbody>
        </table>
        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}
<div>
    @Html.ActionLink("Back to List", "Index")
</div>  


Since the idea is we can add/remove the books in the client side, then only submit it to the server after we are satisfied. The client script looks like this:

<script language="javascript" type="text/javascript">
        function addRow() {

            @{
                Model.Books.Add(new Book());
            }

            var index = $("#tbBooks").children("tr").length;

            var indexCell = "<td style='display:none'><input name='Books.Index' 
            type='hidden' value='" + index + "' /></td>";
            var titleCell = "<td><input id='Books_" + index + "__Title' 
            name='Books[" + index + "].Title' type='text' value='' /></td>";
            var publishedCell = "<td><input id='Books_" + index + "__Title' 
            name='Books[" + index + "].PublishedDate' type='text' value='' /></td>";
            var removeCell = "<td><input id='btnAddBook' type='button' 
            value='Remove' onclick='removeRow(" + index + ");' /></td>";

            var newRow = "<tr id='trBook" + index + "'>" + 
            indexCell + titleCell + publishedCell + removeCell + "</tr>";
            $("#tbBooks").append(newRow);
        }

        function removeRow(id) {
            var controlToBeRemoved = "#trBook" + id;
            $(controlToBeRemoved).remove();
        }
    </script>

Note that the hidden value part below is necessary to allow arbitrary indices.

 var indexCell = "<td style='display:none'>
    <input name='Books.Index' type='hidden' value='" + index + "' /></td>";

If we didn't add this extra hidden input, removing n index in the collection will also remove the subsequent items; for example: if you have 3 items in the collection, removing item number 2 will also remove item number 3.

Now let us add code in the controller to display the result:

 [HttpPost]
 public string Index(Author author)
 {
            var sb = new StringBuilder();
            try
            {
                sb.AppendFormat("Author : {0}", author.Name);
                sb.AppendLine("<br />");
                sb.AppendLine("--------------------------------");
                sb.AppendLine("<br />");
                foreach (var book in author.Books)
                {
                    sb.AppendFormat("Title : {0} | Published Date : {1}", book.Title, book.PublishedDate);
                    sb.AppendLine("<br />");
                }
            }
            catch(Exception ex)
            {
                throw ex;
            }

            return sb.ToString();
 }

Observe that the controller only accepts Author model as the parameter.

Here are some of the results:

Delete the second item:

That's it! Hope it helps.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Marthayaputra
Software Developer
Indonesia Indonesia
No Biography provided
Follow on   LinkedIn

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web02 | 2.8.140827.1 | Last Updated 6 May 2014
Article Copyright 2014 by Marthayaputra
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid