MVC 5 динамических строк с BeginCollectionItem
Каков наилучший способ добавления / удаления строк в таблице при нажатии кнопки? Мне нужны строки, созданные из свойств ChildClass (дочерний класс - это список в основном классе / модели).
В настоящее время есть представление (модель MyMain), которое ссылается на частичное представление с использованием RenderPartial.
Частичное представление отображает свойства модели, класса MyChild, который является списком объектов в MyMain.
Я хочу иметь кнопки добавления и удаления для динамического добавления строк, которые содержатся в частичном представлении.
Поэтому добавление MyChild несколько раз для большего количества строк в списке. Это возможно? Или я не должен использовать частичные представления для этого?
Обновленный код
Ниже приведены текущие классы и представления, с которыми я работаю. Я пытался реализовать помощник BeginCollectionItem, но я получаю нулевую ссылку, в которой я пытаюсь загрузить частичное представление, несмотря на то, что в операторе if говорится о создании нового экземпляра. дочернего класса, если не существует - почему это игнорируется?
Главный вид
@using (Html.BeginForm())
{
<table>
<tr>
<th>MyMain First</th>
<th>MyChild First</th>
</tr>
<tr>
<td>
@Html.EditorFor(m => m.First)
</td>
<td>
@if (Model.child != null)
{
for (int i = 0; i < Model.child.Count; i++)
{
Html.RenderPartial("MyChildView");
}
}
else
{
Html.RenderPartial("MyChildView", new MvcTest.Models.MyChild());
}
</td>
</tr>
@Html.ActionLink("Add another", "Add", null, new { id = "addItem" })
</table>
}
Частичный вид
@model MvcTest.Models.MyChild
@using (Html.BeginCollectionItem("myChildren"))
{
Html.EditorFor(m => m.Second);
}
модели
public class MyMain
{
[Key]
public int Id { get; set; }
public string First { get; set; }
public List<MyChild> child { get; set; }
}
public class MyChild
{
[Key]
public int Id { get; set; }
public string Second { get; set; }
}
контроллер
public class MyMainsController : Controller
{
// GET: MyMains
public ActionResult MyMainView()
{
return View();
}
[HttpPost]
public ActionResult MyMainView(IEnumerable<MyChild> myChildren)
{
return View("MyMainView", myChildren);
}
public ViewResult Add()
{
return View("MyChildView", new MyChild());
}
}
2 ответа
Обновленный ответ - исходный код НЕ верен "динамическому", однако он учитывает все, что мне нужно было сделать в параметрах вопроса.
Первоначально я не мог получить предложение Стивена BCI в комментариях к вопросу, с тех пор у меня есть, и это блестяще. Код ниже в обновленном разделе будет работать, если вы копируете + вставляете, но вам нужно будет либо вручную загрузить BCI из GIT, либо использовать PM> Install-Package BeginCollectionItem
с помощью диспетчера пакетов в Visual Studio.
У меня были некоторые проблемы с различными точками, использующими BCI из-за сложности и из-за того, что я не делал MVC раньше - вот больше информации о работе с доступом class.property(type class).property(type class).property
,
Оригинальный ответ - я привел более четкий пример ниже, чем в моем вопросе, который быстро запутался.
Используя два частичных представления, одно для списка сотрудников, а другое для создания нового сотрудника, все содержится в viewmodel companyemployee, который содержит объект компании и список объектов сотрудников. Таким образом, несколько сотрудников могут быть добавлены, отредактированы или удалены из списка.
Надеемся, что этот ответ поможет всем, кто ищет что-то подобное, он должен предоставить достаточно кода для его работы и, по крайней мере, подтолкнуть вас в правильном направлении.
Я оставил свои классы контекста и инициализатора, так как они вначале верны только для кода, при необходимости я могу добавить их.
Спасибо всем, кто помог.
Модели - CompanyEmployee, являющийся моделью представления
public class Company
{
[Key]
public int id { get; set; }
[Required]
public string name { get; set; }
}
public class Employee
{
[Key]
public int id { get; set; }
[Required]
public string name { get; set; }
[Required]
public string jobtitle { get; set; }
[Required]
public string number { get; set; }
[Required]
public string address { get; set; }
}
public class CompanyEmployee
{
public Company company { get; set; }
public List<Employee> employees { get; set; }
}
Индекс
@model MMV.Models.CompanyEmployee
@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index</h2>
<fieldset>
<legend>Company</legend>
<table class="table">
<tr>
<th>@Html.LabelFor(m => m.company.name)</th>
</tr>
<tr>
<td>@Html.EditorFor(m => m.company.name)</td>
</tr>
</table>
</fieldset>
<fieldset>
<legend>Employees</legend>
@{Html.RenderPartial("_employeeList", Model.employees);}
</fieldset>
<fieldset>
@{Html.RenderPartial("_employee", new MMV.Models.Employee());}
</fieldset>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Submit" class="btn btn-default" />
</div>
</div>
Частичный просмотр списка сотрудников
@model IEnumerable<MMV.Models.Employee>
@using (Html.BeginForm("Employees"))
{
<table class="table">
<tr>
<th>
Name
</th>
<th>
Job Title
</th>
<th>
Number
</th>
<th>
Address
</th>
<th></th>
</tr>
@foreach (var emp in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => emp.name)
</td>
<td>
@Html.DisplayFor(modelItem => emp.jobtitle)
</td>
<td>
@Html.DisplayFor(modelItem => emp.number)
</td>
<td>
@Html.DisplayFor(modelItem => emp.address)
</td>
<td>
<input type="submit" formaction="/Employees/Edit/@emp.id" value="Edit"/>
<input type="submit"formaction="/Employees/Delete/@emp.id" value="Remove"/>
</td>
</tr>
}
</table>
}
Частичное представление Создать сотрудника
@model MMV.Models.Employee
@using (Html.BeginForm("Create","Employees"))
{
<table class="table">
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<tr>
<td>
@Html.EditorFor(model => model.name)
@Html.ValidationMessageFor(model => model.name, "", new { @class = "text-danger" })
</td>
<td>
@Html.EditorFor(model => model.jobtitle)
@Html.ValidationMessageFor(model => model.jobtitle)
</td>
<td>
@Html.EditorFor(model => model.number)
@Html.ValidationMessageFor(model => model.number, "", new { @class = "text-danger" })
</td>
<td>
@Html.EditorFor(model => model.address)
@Html.ValidationMessageFor(model => model.address, "", new { @class = "text-danger" })
</td>
</tr>
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
}
Контроллер - я использовал несколько, но вы можете поместить их все в один
public class CompanyEmployeeController : Controller
{
private MyContext db = new MyContext();
// GET: CompanyEmployee
public ActionResult Index()
{
var newCompanyEmployee = new CompanyEmployee();
newCompanyEmployee.employees = db.EmployeeContext.ToList();
return View(newCompanyEmployee);
}
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(int id)
{
Employee employee = db.EmployeeContext.Find(id);
db.EmployeeContext.Remove(employee);
db.SaveChanges();
return RedirectToAction("Index", "CompanyEmployee");
}
[HttpPost]
public ActionResult Create([Bind(Include = "id,name,jobtitle,number,address")] Employee employee)
{
if (ModelState.IsValid)
{
db.EmployeeContext.Add(employee);
db.SaveChanges();
return RedirectToAction("Index", "CompanyEmployee");
}
return View(employee);
}
}
Обновленный код - использование BeginCollectionItem - динамическое добавление / удаление
Студенческая Частичная
@model UsefulCode.Models.Person
<div class="editorRow">
@using (Html.BeginCollectionItem("students"))
{
<div class="ui-grid-c ui-responsive">
<div class="ui-block-a">
<span>
@Html.TextBoxFor(m => m.firstName)
</span>
</div>
<div class="ui-block-b">
<span>
@Html.TextBoxFor(m => m.lastName)
</span>
</div>
<div class="ui-block-c">
<span>
<span class="dltBtn">
<a href="#" class="deleteRow">X</a>
</span>
</span>
</div>
</div>
}
</div>
Частичный Учитель
@model UsefulCode.Models.Person
<div class="editorRow">
@using (Html.BeginCollectionItem("teachers"))
{
<div class="ui-grid-c ui-responsive">
<div class="ui-block-a">
<span>
@Html.TextBoxFor(m => m.firstName)
</span>
</div>
<div class="ui-block-b">
<span>
@Html.TextBoxFor(m => m.lastName)
</span>
</div>
<div class="ui-block-c">
<span>
<span class="dltBtn">
<a href="#" class="deleteRow">X</a>
</span>
</span>
</div>
</div>
}
</div>
Контроллер регистра
public ActionResult Index()
{
var register = new Register
{
students = new List<Person>
{
new Person { firstName = "", lastName = "" }
},
teachers = new List<Person>
{
new Person { lastName = "", firstName = "" }
}
};
return View(register);
}
Регистрация и модель лица
public class Register
{
public int id { get; set; }
public List<Person> teachers { get; set; }
public List<Person> students { get; set; }
}
public class Person
{
public int id { get; set; }
public string firstName { get; set; }
public string lastName { get; set; }
}
Индекс
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
@model UsefulCode.Models.Register
<div id="studentList">
@using (Html.BeginForm())
{
<div id="editorRowsStudents">
@foreach (var item in Model.students)
{
@Html.Partial("StudentView", item)
}
</div>
@Html.ActionLink("Add", "StudentManager", null, new { id = "addItemStudents", @class = "button" });
}
</div>
<div id="teacherList">
@using (Html.BeginForm())
{
<div id="editorRowsTeachers">
@foreach (var item in Model.teachers)
{
@Html.Partial("TeacherView", item)
}
</div>
@Html.ActionLink("Add", "TeacherManager", null, new { id = "addItemTeachers", @class = "button" });
}
</div>
@section scripts {
<script type="text/javascript">
$(function () {
$('#addItemStudents').on('click', function () {
$.ajax({
url: '@Url.Action("StudentManager")',
cache: false,
success: function (html) { $("#editorRowsStudents").append(html); }
});
return false;
});
$('#editorRowsStudents').on('click', '.deleteRow', function () {
$(this).closest('.editorRow').remove();
});
$('#addItemTeachers').on('click', function () {
$.ajax({
url: '@Url.Action("TeacherManager")',
cache: false,
success: function (html) { $("#editorRowsTeachers").append(html); }
});
return false;
});
$('#editorRowsTeachers').on('click', '.deleteRow', function () {
$(this).closest('.editorRow').remove();
});
});
</script>
}
Действие StudentManager:
public PartialViewResult StudentManager()
{
return PartialView(new Person());
}
Вы можете использовать частичные представления для него, но проблема в том, что генерация свойств Id с EditorFor
вызывает создание дубликатов.
То, что работало для меня, использовало var view = @Html.Raw(@Html.Partial('Your partial'))
внутри javascript в моем основном виде.
Затем эта переменная будет иметь вид с идентификаторами по умолчанию, затем я заменю свойство id и свойство name на id=YourMainListName_{ItemNumber}__Property
а также name=YourMainListName_{ItemNumber}__Property
куда {ItemNumber}
будет индекс нового элемента в списке.
Требуется много javascript, чтобы заставить это работать правильно, но если у вас правильно установлены свойства Id и Name, все должно работать правильно, включая привязку модели при отправке обратной страницы.