En utilisant les informations contenues dans la réponse que Masound affichée, j'ai pu créer une solution à ce problème. Il s'avère que le problème est que j'utilisais une collection séquentielle où chaque index repose sur le précédent. Ainsi, lorsque j'ai supprimé un élément de la collection, j'ai perdu le lien vers les autres éléments qui le traitent.
La solution consiste à utiliser une collection non séquentielle où l'index est stocké en tant que champ masqué dans le DOM. En utilisant cette méthode, MVC sait où se situe chaque index et peut recréer la collection en post avec tous les éléments.
Une description de la solution est la suivante:
- J'ai une vue qui utilise une collection d'objets d'essai comme modèle.
- J'ai une classe auxiliaire qui génère l'index d'un objet dans une collection.
- J'appelle "EditorFor" pour afficher chaque objet Test et son index dans la collection.
- Dans le contrôleur, je remplis le modèle avec 7 éléments de chaîne et le passe à la vue.
- La vue rend les 7 éléments.
- J'utilise jquery pour supprimer le 2ème élément de la collection du DOM.
- Je clique sur Envoyer pour poster.
- Je vérifie le contenu de mon modèle de retour posté et il ne contient que 6 articles, ce qui est ce que j'attends.
- Je fais une redirection vers la méthode Get pour actualiser la liaison de l'état du modèle. J'ai remarqué un comportement où l'indexation se lierait au mauvais élément de la collection. Donc, si vous envisagez de recharger la même vue, je vous recommande d'implémenter cette solution pour éviter les comportements inattendus.
Le code de cette solution est la suivante:
Test.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
namespace WebApplication2.Models
{
public class Test
{
[Required(ErrorMessage="Required")]
public string str {get; set;}
}
}
Test.cshtml
@model WebApplication2.Models.Test
@using WebApplication2.Helpers
@{
ViewBag.Title = "Test";
}
<div id="@Model.str">
@Html.TextBoxFor(x => x.str)
@Html.ValidationMessageFor(x => x.str)
@Html.HiddenIndexerInputForModel()
</div>
Index.cshtml
@model ICollection<WebApplication2.Models.Test>
@{
ViewBag.Title = "Home Page";
}
@using (Html.BeginForm("Index", "Home", FormMethod.Post))
{
@Html.EditorFor(x => x)
<input id="btnSubmit" type="submit" value="Submit" />
}
contrôleur
[HttpGet]
public ActionResult Index()
{
List<Test> vm = (List<Test>)Session["Test"];
if (vm == default(List<Test>))
{
vm = new List<Test>
{
new Test { str="one"},
new Test { str="two"},
new Test { str="three"},
new Test { str="four"},
new Test { str="five"},
new Test { str="six"},
new Test { str="seven"}
};
Session.Add("Test", vm);
}
return View(vm);
}
[HttpPost]
public ActionResult Index(List<Test> vm)
{
Session.Add("Test", vm);
return RedirectToAction("Index");
}
HtmlHelper.cs - Aide a été prise à partir d'ici: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/
public static class ListModelBindingExtensions
{
static Regex _stripIndexerRegex = new Regex(@"\[(?<index>\d+)\]", RegexOptions.Compiled);
public static string GetIndexerFieldName(this TemplateInfo templateInfo)
{
string fieldName = templateInfo.GetFullHtmlFieldName("Index");
fieldName = _stripIndexerRegex.Replace(fieldName, string.Empty);
if (fieldName.StartsWith("."))
{
fieldName = fieldName.Substring(1);
}
return fieldName;
}
public static int GetIndex(this TemplateInfo templateInfo)
{
string fieldName = templateInfo.GetFullHtmlFieldName("Index");
var match = _stripIndexerRegex.Match(fieldName);
if (match.Success)
{
return int.Parse(match.Groups["index"].Value);
}
return 0;
}
public static MvcHtmlString HiddenIndexerInputForModel<TModel>(this HtmlHelper<TModel> html)
{
string name = html.ViewData.TemplateInfo.GetIndexerFieldName();
object value = html.ViewData.TemplateInfo.GetIndex();
string markup = String.Format(@"<input type=""hidden"" name=""{0}"" value=""{1}"" />", name, value);
return MvcHtmlString.Create(markup);
}
}
Merci à tous ceux qui ont contribué à l'arrivée de cette solution!