javascript - Una vista parcial que pasa una colección utilizando el helper Html.BeginCollectionItem
asp.net-mvc model-binding (1)
Hice un pequeño proyecto para entender la respuesta de Stephen Muecke aquí: ¿ Enviar la misma vista parcial llamada datos múltiples veces al controlador?
Casi todo funciona El javascript agrega nuevos campos desde la Vista parcial, y puedo decir que están vinculados al modelo por los valores "temp" insertados por el método del controlador para la vista parcial.
Sin embargo, cuando envío los nuevos campos, el método AddRecord () arroja una excepción que muestra que el modelo no se está transfiriendo ("Referencia de objeto no establecida en una instancia de un objeto").
Además, cuando veo el origen de la página, el ayudante BeginCollectionItem está insertando una etiqueta oculta como debería alrededor de la mesa en la vista principal que muestra registros preexistentes, pero no alrededor de los nuevos campos que agrega el javascript.
¿Qué estoy haciendo mal? Soy bastante nuevo en esto, ¡así que gracias por tu paciencia!
Mi vista principal:
@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>
Mi vista parcial:
@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>
}
Mi modelo:
public class CashRecipient
{
public int Id { get; set; }
public string cashAmount { get; set; }
public string recipientName { get; set; }
}
En mi controlador:
[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);
}
Primero, cree un modelo de vista para representar lo que desea editar. Supongo que cashAmount
es un valor monetario, por lo tanto debe ser un decimal (agregue otros atributos de validación y visualización según sea necesario)
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; }
}
A continuación, cree una vista parcial (por ejemplo) _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>
y un método para devolver ese parcial
public PartialViewResult Recipient()
{
return PartialView("_Recipient", new CashRecipientVM());
}
Entonces su método principal GET será
public ActionResult Create()
{
List<CashRecipientVM> model = new List<CashRecipientVM>();
.... // add any existing objects that your editing
return View(model);
}
y su vista será
@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" />
}
e incluirá una secuencia de comandos para agregar el html para un nuevo 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);
});
});
y la secuencia de comandos para eliminar un elemento
$(''.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();
}
});
Y el formulario se publicará nuevamente en
public ActionResult Create(IEnumerable<CashRecipientVM> recipients)