Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add items to MVC model in javascript?

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?

like image 653
eflles Avatar asked Jun 06 '13 06:06

eflles


2 Answers

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

like image 126
Darin Dimitrov Avatar answered Nov 13 '22 08:11

Darin Dimitrov


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.

like image 39
Kevin Fichter Avatar answered Nov 13 '22 08:11

Kevin Fichter