2016-12-19 1 views
1

Mon approche ici peut être entièrement faux, mais j'apprends lentement MVC ... J'ai un formulaire par lequel un utilisateur doit sélectionner un nombre de (ou pas) de modules, basé sur ce modèle:Checkbox vérifié valeur perdue si! ModelState.IsValid

public class MyProductModule 
{ 
    public string ModuleName { get; set; } 
    public bool Checked { get; set; } 
} 

public class ProductRequest 
{ 

    public ProductRequest() 
    { 
     Modules = LoadModules(); 
    } 

    public static List<MyProductModule> LoadModules() 
    { 
     return new List<MyProductModule>() 
     { 
      new MyProductModule() { ModuleName = "Module One", Checked = false }, 
      new MyProductModule() { ModuleName = "Module Two", Checked = false }, 
      new MyProductModule() { ModuleName = "Module Three", Checked = false } 
     }; 
    } 

    [Required] 
    [EmailAddress] 
    public string Email { get; set; } 

    [DisplayName("MyProduct Modules")] 
    public List<MyProductModule> Modules { get; set; } 
} 

Une liste de case est rendue pour afficher chaque module:

@for (int i = 0; i < Model.Modules.Count; i++) 
{ 
    @Html.CheckBoxFor(m => m.Modules[i].Checked) 
    @Html.HiddenFor(m => m.Modules[i].ModuleName) 
    @Html.LabelFor(m => m.Modules[i].Checked, Model.Modules[i].ModuleName) 
} 

Enfin, la forme est traitée comme suit:

[HttpPost] 
[ValidateAntiForgeryToken] 
public ActionResult ProcessRequest(ProductRequest qd) 
{ 
    if (!ModelState.IsValid) 
    { 
     return View("Index", qd); 
    } 
    else 
    { 
     // check email domains 
     List<string> badDomains = new List<string>(); 
     badDomains.Add("gmail"); 
     badDomains.Add("yahoo"); 
     badDomains.Add("hotmail"); 
     foreach (string s in badDomains) 
     { 
      if (qd.Email.Contains(string.Format("@{0}.", s))) 
      { 
       ModelState.AddModelError(string.Empty, string.Format("Please use your work email address.", s)); 
      } 
     } 

     if (!ModelState.IsValid) 
     { 
      return View("Index", qd); 
     } 
     else 
     { 
      // process 
     } 
    } 
} 

Tout fonctionne parfaitement, sauf si, pour une raison quelconque, ma validation côté serveur échoue et que le modèle est renvoyé (return View("Index", qd);). À ce moment-là, la liste des cases à cocher change mystérieusement de ceci:

[x] Module One 
[ ] Module Two 
[ ] Module Three 

... à ceci:

[ ] Module One 
[ ] Module Two 
[ ] Module Three 

Toutes les valeurs de cases à cocher sont perdus. Si j'examine les données affichées brutes dans Firebug, je vois que, pour une raison à la fois vrai et faux sont affichés pour les cases à cocher « checked »:

enter image description here

+0

Que se passe si vous venez de faire 'retour View ("Index")'? –

+0

La vue renvoie l'erreur 500 (référence null) lorsqu'elle tente de lire les propriétés du modèle. – EvilDr

+0

Avez-vous besoin de revenir à l'affichage de l'index? Qu'en est-il juste de 'return View()'? –

Répondre

1

Du côté du serveur, votre variable qd contient la valeurs que vous avez publiées à partir de la vue: Pour chaque MyProductModule, vous publiez uniquement le membre Checked. Lorsque vous utilisez return View("Index", qd);, vous donnez à l'affichage uniquement la valeur que vous avez: Checked membre.

Si vous voulez avoir le membre ModuleName, vous devez afficher à côté du membre Checked

@Html.LabelFor(model => model.Modules, htmlAttributes: new { @class = "required" }) 
@for (int i = 0; i < Model.Modules.Count; i++) 
{ 
    @Html.CheckBoxFor(m => m.Modules[i].Checked) 
    @Html.HiddenFor(m => m.Modules[i].ModuleName) 
    @Html.LabelFor(m => m.Modules[i].Checked, Model.Modules[i].ModuleName) 
} 

Ok, je ne peux pas reproduire votre erreur, mais je peux suggérer une autre façon de écrire: en utilisant un modèle d'éditeur

au lieu de la boucle, utilisez @Html.EditorFor(m => m.Modules) Vous aurez quelque chose comme

@using (Html.BeginForm("ProcessRequest", "Home")) 
{ 
    @Html.AntiForgeryToken() 
    @Html.TextBoxFor(m => m.Email) 
    @Html.EditorFor(m => m.Modules) 
    <input type="submit" value="send" /> 
} 

Ensuite, créez un dossier EditorTemplates et une nouvelle vue nommée MyProductModule.cshtml

enter image description here

MyProductModule.cshtml:

@model MyNamespace.MyProductModule 

@Html.CheckBoxFor(m => m.Checked) 
@Html.HiddenFor(m => m.ModuleName) 
@Html.LabelFor(m => m.Checked, Model.ModuleName) 
+0

Merci. Bien que les noms d'étiquette persistent, la propriété "checked" est perdue, donc toutes les cases sont décochées si le modèle est retourné dans le cas où AddModelError est ajouté – EvilDr

+0

Etrangement, si j'examine les données brutes, je vois ceci: 'Modules [ 2] .Checked = true, Modules [2] .Checked = false, Modules [2] .ModuleName = Module Trois'. Pourquoi la propriété Checked apparaît-elle deux fois avec des valeurs différentes? – EvilDr

+0

Pourriez-vous poster votre cshtml exact s'il vous plait? J'utilise ce code exact et ça marche bien – FBO