MVC 5 BeginCollectionItem с частичным CRUD

Ниже я внес изменения в вопрос, который все тот же, но, надеюсь, намного более понятный по моделям и в отношении того, чего я хочу достичь и где я столкнулся с проблемами.

Ниже показаны два класса, Компания и Сотрудник, Компания имеет список Сотрудников.

Это будет форма ввода, поэтому там не будет никаких данных для начала.

В конечном итоге я хочу, чтобы пользователь мог добавлять столько объектов Employee в объектную модель Company, сколько они хотят, и для объектов Employee, которые будут обновлены.

Я на правильном пути с использованием BeginCollectionItem, чтобы я мог добавить / удалить столько объектов Employee, сколько я хочу? Когда я нажимаю кнопку "Добавить", она переходит в частичное представление на другой странице (с AjaxActionLink), но не с JavaScript.

Обновление Удалил AjaxActionLink и использовал вместо него JavaScript.

Индекс

@model MvcTest.Models.Company
@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Company</h2>
<div>
    @Html.LabelFor(m => m.Name)
    @Html.EditorFor(m => m.Name)
</div>
<fieldset>
    <legend>Employees</legend>
    <div id="new-Employee">
        @foreach (var Employee in Model.Employees)
        {
            Html.RenderPartial("_Employee", Employee);
        }
    </div>
    <div>
        <input type="button" id="addemployee" name="addemployee" value="Add Employee"/>
        <br/>
    </div>
    <br/>
    @section Scripts
    {
        <script type="text/javascript">
            $('#addemployee').on('click', function () {
                $.ajax({
                    async: false,
                    url: '/Company/AddNewEmployee'
                }).success(function (partialView) {
                    $('#new-Employee').append(partialView);
                });
            });
        </script>
    }
</fieldset>
<div>
    <input type="submit" value="Submit" />
</div>

_Employee PartialView

    @model MvcTest.Models.Employee

@using (Html.BeginCollectionItem("Employees"))
{
    <div class="employeeRow">
        @Html.LabelFor(m => m.Name)
        @Html.EditorFor(m => m.Name)

        @Html.LabelFor(m => m.Telephone)
        @Html.EditorFor(m => m.Telephone)

        @Html.LabelFor(m => m.Mobile)
        @Html.EditorFor(m => m.Mobile)

        @Html.LabelFor(m => m.JobTitle)
        @Html.EditorFor(m => m.JobTitle)

        <a href="#" class="deleteRow">Delete</a>
    </div>
}

@section Scripts
{
$("a.deleteRow").live("click", function(){
    $(this).parents("div.employeeRow:first").remove();
return false;
});
}

контроллер

public class CompanyController : Controller
    {
        // GET: Company
        public ActionResult Index()
        {
            var newCompany = new Company();
            return View(newCompany);
        }
        public ActionResult AddNewEmployee()
        {
            var employee = new Employee();
            return PartialView("_Employee", employee);
        }
    }

модель

public class Company
    {
        [Key]
        public int Id { get; set; }
        [Display(Name = "Company")]
        public string Name { get; set; }
        public List<Employee> Employees { get; set; }

        //public Company()
        //{
        //    Employees = new List<Employee>
        //    {
        //        new Employee{ Name = "Enter name"}
        //    };
        //}
    }
    public class Employee
    {
        [Key]
        public int Id { get; set; }
        [Display(Name="Employee")]
        public string Name { get; set; }
        public string Telephone { get; set; }
        public string Mobile {get;set;}
        [Display(Name="Job Title")]
        public string JobTitle {get;set;}
    }

4 ответа

Решение

Вам не нужно использовать BeginCollectionItem для достижения этой цели. Из-за того, что мне пришлось изучать его самостоятельно и пытаться использовать его для решения аналогичной проблемы, оказалось, что он был создан для подобных проблем с более ранними версиями MVC.

Используйте частичные представления для отображения и обновления списка. Один частичный вид для отображения и итерации по списку объектов, а другой для создания нового объекта, который после публикации для обновления списка покажет вновь созданный объект в частичном представлении со списком.

Я разместил подобный вопрос здесь, который должен решить вашу проблему, нажмите здесь

Надеюсь это поможет.

Обновление Причина, по которой ваше удаление не работает, заключается в том, что вы не можете вызвать JS из частичного просмотра, поместите его в главное представление (@section Script). Также я думаю, что вы немного запутались с вашими ключевыми словами class и id в ваших divs, посмотрите ниже.

Итак, вы должны иметь:

Частичный вид

@model MvcTest.Models.Employee
    @using (Html.BeginCollectionItem("Employees"))
    {
        <div id="employeeRow" class="employeeRow">
            @Html.LabelFor(m => m.Name)
            @Html.EditorFor(m => m.Name)

            @Html.LabelFor(m => m.Telephone)
            @Html.EditorFor(m => m.Telephone)

            @Html.LabelFor(m => m.Mobile)
            @Html.EditorFor(m => m.Mobile)

            @Html.LabelFor(m => m.JobTitle)
            @Html.EditorFor(m => m.JobTitle)

            <a href="#" id="deleteRow" class="deleteRow" onclick="deleteFunction()">Delete</a>
        </div>
    }

Главный вид

    @model MvcTest.Models.Company
@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Company</h2>
<div>
    @Html.LabelFor(m => m.Name)
    @Html.EditorFor(m => m.Name)
</div>
<fieldset>
    <legend>Employees</legend>
    <div id="new-Employee">
        @foreach (var Employee in Model.Employees)
        {
            Html.RenderPartial("_Employee", Employee);
        }
    </div>
    <div>
        <input type="button" id="addemployee" name="addemployee" value="Add Employee"/>
        <br/>
    </div>
    <br/>
    @section Scripts
    {
        <script type="text/javascript">
            $('#addemployee').on('click', function () {
                $.ajax({
                    async: false,
                    url: '/Company/AddNewEmployee'
                }).success(function (partialView) {
                    $('#new-Employee').append(partialView);
                });
            });

            $("#deleteRow").live("click", function () {
                $(this).parents("#employeeRow:first").remove();
                return false;
            });
        </script>
    }
</fieldset>
<div>
    <input type="submit" value="Submit" />
</div>

Допустим ClassA ваша ViewModel:

Вам нужно только одно частичное представление или представление, чтобы обновить оба:

например Edit.cshtml

@model ClassA

@Html.BeginForm(){

@Html.TextBoxFor(m => m.name)

for(int i = 0; i< Model.classB.Count; i++){

     @Html.TextBoxFor(m => Model.classB[i].name)
<button type="button"> Add </button>
<button type="button"> Remove </button>
}

<input type="submit"  value = "save"/>

}

Примечание. В частичном представлении вы используете цикл foreach. Для подшивки модели MVC поля ввода должны иметь формат:

list[0].prop1
list[0].prop2
list[0].prop3

list[1].prop1
list[1].prop2
list[1].prop3

Так что для этого мы используем для цикла

Тогда в контроллере:

[HttpPost]
public ActionResult Edit(ClassA model){


// HEre you will see model.ClassB list
// you can then save them one by one

foreach(var item in Model.classB){

save
}
return View(model);
}

Если вы хотите динамически добавлять или удалять элементы из списка:

Создать еще один частичный вид classBs:

        @model List<classB>
        <div id="lists">
            foreach (var contact in Model)
            {
               @Html.Partial("ClassBRow", contact)
            }
    </div>

<button data-action-url="@Url.Action("ClassBRow","CONTROLLER")" class="btn btn-primary pull-right" id="addItem">Add</button>

<script>
 $("#addItem").click(function () {
            var btn = $(this);
            $.ajax({
                url: btn.data('action-url'),
                success: function (html) {
                    $("#lists").append(html);
                }
            });
            return false;
        });

</script>

Создайте еще один частичный вид: ClassBRow.cshtml:

@model classB

 using (Html.BeginCollectionItem("classB"))
    {
            @Html.HiddenFor(m => m.isDeleted, new { data_is_deleted = "false" })
            @Html.TextBoxFor(m => Model.name, new { @class = "form-control" })

<span class="glyphicon glyphicon-trash" data-action="removeItem" title="remove" style="cursor:pointer"></span>

    }

В вашем контроллере:

public ActionResult ClassBRow()
{
    return PartialView(new classB());
}

И Edit.cshtml становится:

    @model ClassA

    @Html.BeginForm(){

    @Html.TextBoxFor(m => m.name)

@Html.Partial("classBs", model.classB)

    <input type="submit"  value = "save"/>

    }

Рассматривайте взгляды как независимые. Если бы ваш частичный просмотр был полным просмотром, вы бы передали его IEnumerable<ClassB>

так рендер с:

 @Html.Partial("ClassBView", Model.ClassB)

Кроме того, вы не можете использовать foreach с EditorFor поскольку недостаточно информации для создания имени на основе индекса. Вместо этого используйте обычный цикл for. Парсер выражений может затем преобразовать его в name="name[1]" и т. д., поскольку индекс доступен в выражении:

@model IEnumerable<Project.Models.ClassB>

@if(Model != null)
{
    for (int i = 0; i < Model.Count; i++)
    {
    <tr>
        <td>
            @Html.EditorFor(modelItem => Model[i].Name)
            <input type="button" value="Clear" />
            <input type="submit" value="Create" />
        </td>
    </tr>
    }
}

В вашем примере больше не хватает (что Clear а также Create подключиться к, например), поэтому, если вы можете предоставить оставшуюся часть кода вашего контроллера, я расширю это.

Вы можете использовать (EditorTemplates) для просмотра (ClassB) следующим образом:

1- Создайте папку с именем (EditorTemplates) в папке (Views/Home) (при условии, что имя вашего контроллера - Home):

2- В созданной папке (EditorTemplates) создайте представление с именем (ClassB)

введите описание изображения здесь

и добавьте следующий шаблон для представления (ClassB):

@model Project.Models.ClassB
@if(Model != null)
{

    <tr>
        <td>
            @Html.EditorFor(modelItem => Model.Name)
            <input type="button" value="Clear" />
            <input type="submit" value="Create" />
        </td>
    </tr>

}

и (ClassAView) должен быть следующим:

@model Project.Models.ClassA

<tr>
<td>
@Html.EditorFor(m => m.name)
</td>
<td>
@Html.EditorFor(m => m.classB);
</td>
</tr>

Редактор автоматически перебирает список объектов, отображающих представление для каждого из них.

Другие вопросы по тегам