8

I would like to add items to a list in my model dynamically with java script. How can I make MVC bind the new item to the model?

My models:

public class Garage
{
    public string Name{ get; set; }
    public string Location { get; set; }
    public IList<Car> Cars{ get; set; }
}

public class Car
{
    public string Color{ get; set; }
    public string Name { get; set; }
}

My view, which uses a Garage as model:

<% using (Html.BeginForm())
{%>
<div id="cars">
         <% 
               foreach (var item in Model.Cars)
               {
                  Html.RenderPartial("CarView", item);
              } %>
</div>
<% } %>

And my CarView which uses a Car as model:

 <div class="carRow">               

        <%--   Color--%>
        <%=Html.CustomLabelFor(model => model.Color)%>
        <%= Html.TextBox(Model.Color) %>

         <%--   Name--%>
        <%=Html.CustomLabelFor(model => model.Name)%>
        <%= Html.TextBox(Model.Name) %>
 </div>

When adding a new Car, I uses a AJAX call, and adds it to the html. The AJAX uses this method in the controller:

 public ViewResult NewCar()
    {
        return View("CarView");
    }

My java script ajax call:

            $('.addCarButton').click(function () {
            $.ajax({
                url: "<%= Url.Action("CreateCars") %>",
                cache: false,
                success: function (html) { $("#cars").append(html); }
            });
            return false;
        });

This renders the html nicely, but it does not add the car to the list of cars.

How can this be done?

1
  • It shoudldn't be needed to add a javascript click handler. These thins are already provided in MVC. Commented Jun 6, 2013 at 6:33

3 Answers 3

9

You may take a look at the following article in which Steven Sanderson provides a step by step tutorial on how to implement this.

Sign up to request clarification or add additional context in comments.

Comments

3

I realize that this is an old question, but after trying the above suggestion re Steven Sanderson's BeginCollectionItem, and a number of other potential solutions, I did not get very far (new items would not post). BeginCollectionItem seemed like a nice solution in theory, but it would not post new items, and it had unexpected effects on formatting of the list items.

The solution wound up being surprisingly simple, and did not require any external libraries (beyond JQuery). This works for me in ASP.NET MVC5.

  1. Create an editor template for the row item.

path: Views/Shared/EditorTemplate/NuggetSourceDto.cshtml

@model [namespace].NuggetSourceDto

@{
    ViewBag.Title = "NuggetSourceDto";
}

<li [email protected]>
    @Html.HiddenFor(t => t.Id)
    @Html.TextBoxFor(s => s.Url, new { @class = "form-control", autofocus = "autofocus" })
    <a role="button" class="glyphicon glyphicon-remove"></a>
</li>
  1. Use the above template (the following runs through the collection and generates html for each item):

In my view:

@Html.EditorFor(m => m.NuggetSources);
  1. When a user clicks the 'add row' button ('addSourceBtn' in my code below), I use ajax to get the html for the template.

MVC controller get method:

[HttpGet]
public PartialViewResult AddBlankSourcesRow()
{
    return PartialView("EditorTemplates/NuggetSourceDto", new NuggetSourceDto());
}

js handler:

$(document).ready(function () {
    $('#addSourceBtn').click(function () {
        var indexOfNewItem = $('#sourceList li').length;

        $.ajax({
            url: '/nugget/AddBlankSourcesRow/',
            cache: false,
            success: function (html) {
                var newItem = $(html);
                var randId = Math.random() * 10000000;
                randId = Math.floor(randId);
                newItem.attr('id', 'newSource__' + randId);
                newItem.find('input').first().attr({
                    //name: 'NuggetSources[3].Id'
                    name: 'NuggetSources[' + indexOfNewItem + '].Id',
                    id: 'NuggetSources_' + indexOfNewItem + '__Id',
                    value: randId
                });
                newItem.find('input[id^=Url]').attr({
                    name: 'NuggetSources[' + indexOfNewItem + '].Url',
                    id: 'NuggetSources_' + indexOfNewItem + '__Url'
                });
                $('#sourceList').append(newItem);
            }
        });
        return false;
    });
});

The lynchpin in all of this is to ensure that the newly-inserted element has a name attribute for each property that includes the name of the collection and a valid index:

        newItem.find('input').first().attr({
            //name: 'NuggetSources[3].Id'
            name: 'NuggetSources[' + indexOfNewItem + '].Id'
        });
        newItem.find('input[id^=Url]').attr({
            name: 'NuggetSources[' + indexOfNewItem + '].Url'
        });

Without this, the new items are ignored in the MVC controller.

This solution only handles adding new rows. For deletions, because the indexing is important, one solution is to fire a delete request to the server and then reload the list, or just fix the existing indices in js.

Comments

0

Follow this jQuery plugin , specially designed for adding new element client side in detailed part of transaction

https://github.com/suraj-mahajan/Add-New-Row-jQuery-for-MVC

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.