Actualmente estoy trabajando en una aplicación ASP.NET MVC 4.5. Quiero agregar objetos a una List<T> dinámicamente con un clic de botón, usando un @Html.EditorFor y enviar los datos al controlador con un envío de formulario.
Sin embargo, mi problema es que solo el primer elemento de la lista completa se envía al controlador cuando envío mi formulario.
El área en mi vista donde agrego Elementos se ve así:
<div id="addTarget" data-target="@Url.Action("AddTarget", "Offer")"> @for(var i = 0; i < Model.Targets.Count(); i++) { @Html.EditorFor(m => m.Targets[i]) } </div>Mi controlador se ve así:
... public OfferVm NewOffer { get; set; } = new OfferVm(); [HttpGet] public ActionResult Create() { // ...here I add my first initial target to OfferVm NewOffer.Targets.Add(new TargetVm()); return View(NewOffer); } public ActionResult AddTarget() { // ...here I add a target to my NewOffer NewOffer.Targets.Add(new TargetVm()); return PartialView("~/Views/Shared/EditorTemplates/TargetVm.cshtml"); } [HttpPost] public ActionResult Create(OfferVm offerVM) { //... here only 1 target is in my offerVM }Mi clase OfferVm se ve así:
public class OfferVm { public OfferVm() { this.Targets = new List<TargetVm>(); } public List<TargetVm> Targets { get; set; } }¿Tiene alguna idea de cómo puedo agregar un nuevo objetivo a mi NewOffer.Targets List y obtener los datos en mi controlador cuando publique el formulario?
La razón por la que solo el primer TargetVm en su colección está vinculado en el método POST es que su método AddTarget() al que llama para agregar un nuevo elemento está generando controles de formulario con un name que es idéntico a los que ha generado previamente. Suponiendo TargetVm tiene una propiedad llamada ID , entonces tiene varios elementos con
<input ... name="Targets[0].ID" ... /> y DefaultModelBinder solo vincula el primer par de nombre/valor de la solicitud e ignora cualquier nombre duplicado posterior. Para vincularse a una colección, los atributos de su name deben tener indexadores que comiencen en cero y sean consecutivos.
<input ... name="Targets[0].ID" ... /> // first TargetVM <input ... name="Targets[1].ID" ... /> // second TargetVM <input ... name="Targets[2].ID" ... /> // third TargetVM Tenga en cuenta que también puede incluir una entrada adicional para cada elemento de la colección: <input type="hidden" name="Targets.Index value="xx" /> (donde xx es el valor del indexador) para enlazar no consecutivos indizadores como se describe en Vinculación de modelos a una lista .
El uso del método EditorFor() no funcionará en el caso de que agregue dinámicamente nuevos elementos de colección, a menos que use javascript para actualizar el valor del indexador en el atributo de name cuando se agregue.
Las soluciones incluyen el uso de un marco MVVM de javascript como knockout , el uso del ayudante BeginCollectionItem() o el uso de una plantilla del lado del cliente y javascript para agregar dinámicamente nuevos controles de formulario. Las últimas dos opciones se analizan en esta respuesta , y en esta respuesta se muestra un ejemplo más completo que usa BeginCollectionItem() .