Partial view passing a collection using the Html.BeginCollectionItem helper

I did a small project to understand the answer from Stephen Muecke here: Send the same Partial View, which is called several times the data for the controller?

Almost everything works. Javascript adds new fields from Partial View, and I can say that they are bound to the model with the "temp" values ​​inserted by the controller method for partial viewing.

However, when I submit new fields, the AddRecord () method throws an exception indicating that the model is not being transmitted ("Object reference not set to object instance").

In addition, when I look at the source of the page, the BeginCollectionItem helper inserts a hidden tag, as it should be around the table in the main view, which displays previously existing records, but not around the new fields that javascript adds.

What am I doing wrong? I am new to this, so thanks for your patience!

My main view:

@model IEnumerable<DynamicForm.Models.CashRecipient> @using (Html.BeginForm("AddDetail", "CashRecipients", FormMethod.Post)) { @Html.AntiForgeryToken() <div id="CSQGroup"> </div> } <div> <input type="button" value="Add Field" id="addField" onclick="addFieldss()" /> </div> <script> 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"); } }); } </script> 

My partial view:

 @model DynamicForm.Models.CashRecipient @using HtmlHelpers.BeginCollectionItem @using (Html.BeginCollectionItem("recipients")) { <div class="editor-field"> @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) </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" value="Save" class="btn btn-default" /> </div> </div> } 

My model:

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

In my controller:

 [HttpPost] [ValidateAntiForgeryToken] public ActionResult AddDetail([Bind(Include = "Id,cashAmount,recpientName")] IEnumerable<CashRecipient> 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); } 
+10
source share
1 answer

Start by creating a view model to represent what you want to edit. I assume cashAmount is a monetary value, so it should be decimal (add other validation and display attributes if necessary)

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

Then create a partial view (say) of _Recipient.cshtml

 @model CashRecipientVM <div class="recipient"> @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) <button type="button" class="delete">Delete</button> } </div> 

and method of returning this partial

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

Then your main get method will be

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

and his presentation will be

 @model IEnumerable<CashRecipientVM> @using (Html.BeginForm()) { <div id="recipients"> foreach(var recipient in Model) { @Html.Partial("_Recipient", recipient) } </div> <button id="add" type="button">Add</button> <input type="submit" value="Save" /> } 

and will include a script to add html for the new 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); }); }); 

and script to delete an 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(); } }); 

And the form will return back to

 public ActionResult Create(IEnumerable<CashRecipientVM> recipients) 
+18
source

All Articles