Linhas dinâmicas do MVC 5 com BeginCollectionItem

Qual é a melhor maneira de adicionar / excluir linhas a uma tabela quando um botão é clicado? Eu preciso de linhas criadas a partir de propriedades ChildClass (class filho é uma lista dentro da class principal / modelo).

Atualmente tem uma View (o modelo é MyMain) que faz referência a uma Vista Parcial usando RenderPartial.

A visão parcial exibe as propriedades do modelo, uma class chamada MyChild que é uma lista de objects dentro do MyMain.

Eu quero ter adicionar e excluir botões para adicionar dinamicamente as linhas que são mantidas dentro da visão parcial.

Então adicionando MyChild repetidamente para mais linhas na lista. Isso é possível? Ou eu não deveria estar usando visualizações parciais para isso?

Código atualizado

Abaixo estão as classs e exibições atuais com as quais estou trabalhando, estou tentando implementar o auxiliar BeginCollectionItem, mas estou obtendo a referência nula onde estou tentando carregar a exibição parcial, apesar da declaração if dizer para criar uma nova instância da class filho, se não existe – por que isso está sendo ignorado?

Vista principal

@using (Html.BeginForm()) {  @Html.ActionLink("Add another", "Add", null, new { id = "addItem" }) 
MyMain First MyChild First
@Html.EditorFor(m => m.First) @if (Model.child != null) { for (int i = 0; i < Model.child.Count; i++) { Html.RenderPartial("MyChildView"); } } else { Html.RenderPartial("MyChildView", new MvcTest.Models.MyChild()); }
}

Vista parcial

 @model MvcTest.Models.MyChild @using (Html.BeginCollectionItem("myChildren")) { Html.EditorFor(m => m.Second); } 

Modelos

 public class MyMain { [Key] public int Id { get; set; } public string First { get; set; } public List child { get; set; } } public class MyChild { [Key] public int Id { get; set; } public string Second { get; set; } } 

Controlador

 public class MyMainsController : Controller { // GET: MyMains public ActionResult MyMainView() { return View(); } [HttpPost] public ActionResult MyMainView(IEnumerable myChildren) { return View("MyMainView", myChildren); } public ViewResult Add() { return View("MyChildView", new MyChild()); } } 

Resposta atualizada – O código original NÃO é verdadeiro para ‘dynamic’, no entanto, permite tudo o que eu precisava fazer dentro dos parâmetros da pergunta.

Inicialmente, eu não consegui obter a sugestão de BCI de Stephen na pergunta de comentários trabalhando, desde então eu tenho e é shiny. O código abaixo na seção atualizada funcionará se você copiar + colar, mas será necessário baixar manualmente o BCI do GIT ou usar PM> Install-Package BeginCollectionItem com o Console do Gerenciador de Pacotes no Visual Studio.

Eu tive algum problema com vários pontos usando BCI devido à complexidade e não ter feito MVC antes – aqui está mais informações sobre como lidar com a class.property(type class).property(type class).property .

Resposta original – Eu fui com um exemplo mais claro abaixo do que na minha pergunta que rapidamente ficou muito confusa.

Usando duas visões parciais, uma para a lista de empregados e outra para a criação de um novo empregado, tudo contido no modelo de visão da empresa empregado que contém um object da empresa e uma lista de objects do empregado. Dessa forma, vários funcionários podem ser adicionados, editados ou excluídos da lista.

Espero que esta resposta ajude alguém procurando por algo semelhante, isso deve fornecer código suficiente para fazê-lo funcionar e, no mínimo, empurrá-lo na direção certa.

Eu deixei de fora as minhas classs de contexto e boot, pois elas só são verdadeiras para codificar primeiro, se necessário, posso adicioná-las.

Obrigado a todos que ajudaram.

Modelos – CompanyEmployee sendo o modelo de exibição

 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 employees { get; set; } } 

Índice

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

Index

Company
@Html.LabelFor(m => m.company.name)
@Html.EditorFor(m => m.company.name)
Employees @{Html.RenderPartial("_employeeList", Model.employees);}
@{Html.RenderPartial("_employee", new MMV.Models.Employee());}

Vista Parcial da Lista de Funcionários

 @model IEnumerable @using (Html.BeginForm("Employees")) {  @foreach (var emp in Model) {  } 
Name Job Title Number Address
@Html.DisplayFor(modelItem => emp.name) @Html.DisplayFor(modelItem => emp.jobtitle) @Html.DisplayFor(modelItem => emp.number) @Html.DisplayFor(modelItem => emp.address)
}

Vista parcial Criar empregado

 @model MMV.Models.Employee @using (Html.BeginForm("Create","Employees")) {  @Html.ValidationSummary(true, "", new { @class = "text-danger" }) 
@Html.EditorFor(model => model.name) @Html.ValidationMessageFor(model => model.name, "", new { @class = "text-danger" }) @Html.EditorFor(model => model.jobtitle) @Html.ValidationMessageFor(model => model.jobtitle) @Html.EditorFor(model => model.number) @Html.ValidationMessageFor(model => model.number, "", new { @class = "text-danger" }) @Html.EditorFor(model => model.address) @Html.ValidationMessageFor(model => model.address, "", new { @class = "text-danger" })
}

Controller – Eu usei vários, mas você pode colocá-los todos em um

 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); } } 

Código atualizado – usando BeginCollectionItem – dynamic add / delete

Estudante Parcial

 @model UsefulCode.Models.Person 
@using (Html.BeginCollectionItem("students")) {
@Html.TextBoxFor(m => m.firstName)
@Html.TextBoxFor(m => m.lastName)
X
}

Professor Parcial

 @model UsefulCode.Models.Person 
@using (Html.BeginCollectionItem("teachers")) {
@Html.TextBoxFor(m => m.firstName)
@Html.TextBoxFor(m => m.lastName)
X
}

Registrar controlador

 public ActionResult Index() { var register = new Register { students = new List { new Person { firstName = "", lastName = "" } }, teachers = new List { new Person { lastName = "", firstName = "" } } }; return View(register); } 

Register e Person Model

 public class Register { public int id { get; set; } public List teachers { get; set; } public List students { get; set; } } public class Person { public int id { get; set; } public string firstName { get; set; } public string lastName { get; set; } } 

Índice

 @{ Layout = "~/Views/Shared/_Layout.cshtml"; } @model UsefulCode.Models.Register 
@using (Html.BeginForm()) {
@foreach (var item in Model.students) { @Html.Partial("StudentView", item) }
@Html.ActionLink("Add", "StudentManager", null, new { id = "addItemStudents", @class = "button" }); }
@using (Html.BeginForm()) {
@foreach (var item in Model.teachers) { @Html.Partial("TeacherView", item) }
@Html.ActionLink("Add", "TeacherManager", null, new { id = "addItemTeachers", @class = "button" }); }
@section scripts { }

Ação do StudentManager:

 public PartialViewResult StudentManager() { return PartialView(new Person()); } 

Você pode usar EditorFor parciais para ele, mas o problema é que gerar as propriedades de Id com o EditorFor causar duplicatas a serem geradas.

O que funcionou para mim foi usando var view = @Html.Raw(@Html.Partial('Your partial')) dentro do javascript na minha visão principal.

Essa variável terá a view com os ids padrão, então substituo a propriedade id e a propriedade name por id=YourMainListName_{ItemNumber}__Property e name=YourMainListName_{ItemNumber}__Property

Onde {ItemNumber} será o índice do novo item na lista.

É preciso muito JavaScript para que isso funcione corretamente, mas contanto que você tenha as propriedades Id e Name configuradas corretamente, tudo deve funcionar corretamente, incluindo a vinculação de modelo ao publicar sua página novamente.