Uma exibição parcial passando uma coleção usando o auxiliar Html.BeginCollectionItem

Eu fiz um pequeno projeto para entender a resposta de Stephen Muecke aqui: Enviar mesma vista parcial chamado vários dados de tempos para o controlador?

Quase tudo funciona. O javascript adiciona novos campos da Vista Parcial, e posso dizer que eles estão vinculados ao modelo pelos valores “temp” inseridos pelo método do controlador para a vista parcial.

No entanto, quando eu envio os novos campos, o método AddRecord () lança uma exceção mostrando que o modelo não está sendo passado (“Referência de object não definida para uma instância de um object”).

Além disso, quando vejo a origem da página, o auxiliar BeginCollectionItem está inserindo uma marca oculta como deveria ao redor da tabela na exibição principal que exibe registros pré-existentes, mas não em torno dos novos campos que o JavaScript adiciona.

O que estou fazendo de errado? Eu sou muito novo nisso, obrigado pela sua paciência!

Minha visão principal

@model IEnumerable @using (Html.BeginForm("AddDetail", "CashRecipients", FormMethod.Post)) { @Html.AntiForgeryToken() 
}
function addFieldss() { //alert("ajax call"); $.ajax({ url: '@Url.Content("~/CashRecipients/RecipientForm")', type: 'GET', success:function(result) { //alert("Success"); var newDiv = document.createElement("div"); var newContent = document.createTextNode("Hi there and greetings!"); newDiv.appendChild(newContent); newDiv.innerHTML = result; var currentDiv = document.getElementById("div1"); document.getElementById("CSQGroup").appendChild(newDiv); }, error: function(result) { alert("Failure"); } }); }

Minha visão parcial:

 @model DynamicForm.Models.CashRecipient @using HtmlHelpers.BeginCollectionItem @using (Html.BeginCollectionItem("recipients")) { 
@Html.LabelFor(model => model.Id) @Html.LabelFor(model => model.cashAmount) @Html.TextBoxFor(model => model.cashAmount) @Html.LabelFor(model => model.recipientName) @Html.TextBoxFor(model => model.recipientName)
}

Meu modelo:

 public class CashRecipient { public int Id { get; set; } public string cashAmount { get; set; } public string recipientName { get; set; } } 

No meu controlador:

 [HttpPost] [ValidateAntiForgeryToken] public ActionResult AddDetail([Bind(Include = "Id,cashAmount,recpientName")] IEnumerable cashRecipient) { if (ModelState.IsValid) { foreach (CashRecipient p in cashRecipient) { db.CashRecipients.Add(p); } db.SaveChanges(); return RedirectToAction("Index"); } return View(cashRecipient); } public ActionResult RecipientForm() { var data = new CashRecipient(); data.cashAmount = "temp"; data.recipientName = "temp"; return PartialView(data); } 

Primeiro comece criando um modelo de exibição para representar o que você deseja editar. Estou assumindo que cashAmount é um valor monetário, portanto, deve ser um decimal (adicionar outros atributos de validação e exibição, conforme necessário)

 public class CashRecipientVM { public int? ID { get; set; } public decimal Amount { get; set; } [Required(ErrorMessage = "Please enter the name of the recipient")] public string Recipient { get; set; } } 

Em seguida, crie uma vista parcial (digamos) _Recipient.cshtml

 @model CashRecipientVM 
@using (Html.BeginCollectionItem("recipients")) { @Html.HiddenFor(m => m.ID, new { @class="id" }) @Html.LabelFor(m => m.Recipient) @Html.TextBoxFor(m => m.Recipient) @Html.ValidationMesssageFor(m => m.Recipient) @Html.LabelFor(m => m.Amount) @Html.TextBoxFor(m => m.Amount) @Html.ValidationMesssageFor(m => m.Amount) }

e um método para retornar essa parcial

 public PartialViewResult Recipient() { return PartialView("_Recipient", new CashRecipientVM()); } 

Então seu principal método GET será

 public ActionResult Create() { List model = new List(); .... // add any existing objects that your editing return View(model); } 

e sua visão será

 @model IEnumerable @using (Html.BeginForm()) { 
foreach(var recipient in Model) { @Html.Partial("_Recipient", recipient) }
}

e includeá um script para adicionar o html para um novo CashRecipientVM

 var url = '@Url.Action("Recipient")'; var form = $('form'); var recipients = $('#recipients'); $('#add').click(function() { $.get(url, function(response) { recipients.append(response); // Reparse the validator for client side validation form.data('validator', null); $.validator.unobtrusive.parse(form); }); }); 

e o script para excluir um item

 $('.delete').click(function() { var container = $(this).closest('.recipient'); var id = container.find('.id').val(); if (id) { // make ajax post to delete item $.post(yourDeleteUrl, { id: id }, function(result) { container.remove(); }.fail(function (result) { // Oops, something went wrong (display error message?) } } else { // It never existed, so just remove the container container.remove(); } }); 

E o formulário será postado de volta para

 public ActionResult Create(IEnumerable recipients)