问题
I made a small project to understand the answer from Stephen Muecke here: Submit same Partial View called multiple times data to controller?
Almost everything works. The javascript adds new fields from the Partial View, and I can tell they\'re bound to the model by the \"temp\" values inserted by the controller method for the partial view.
However, when I submit the new fields the AddRecord() method throws an exception showing that the model isn\'t getting passed in (\"Object reference not set to an instance of an object\").
Also, when I view the page source, the BeginCollectionItem helper is inserting a hidden tag as it should around the table in the main view that displays pre-existing records, but not around the new fields that the javascript adds.
What am I doing wrong? I\'m pretty new at 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);
}
回答1:
First start by creating a view model to represent what you want to edit. I'm assuming cashAmount
is a monetary value, so therefore should be a decimal (add other validation and display attributes as required)
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) _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 a method to return that 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 its view 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 the html for a 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 the 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 post back to
public ActionResult Create(IEnumerable<CashRecipientVM> recipients)
来源:https://stackoverflow.com/questions/40539321/a-partial-view-passing-a-collection-using-the-html-begincollectionitem-helper