2010-05-26 15 views
10

Une fois de plus, je suis confronté à une situation "Cela ne devrait pas être ce? *! # Dur".Liaison à une SelectList dans MVC

Problème: Je souhaite utiliser un formulaire dans MVC pour la création d'un objet. Un des éléments de l'objet est un ensemble de choix limités - un candidat idéal pour une liste déroulante. Mais si j'utilise une liste SelectList dans mon modèle et une liste déroulante dans ma vue, puis que j'essaie de renvoyer le modèle à ma méthode Create, j'obtiens l'erreur "Exception de méthode manquante: aucun constructeur sans paramètre pour cette objet". En explorant le code source MVC, il semble que pour se lier à un modèle, le classeur doit être capable de le créer en premier, et il ne peut pas créer une SelectList car il n'y a pas de constructeur par défaut pour cela.

Voici le code simplifié: Pour le modèle:

public class DemoCreateViewModel 
{ 
    public SelectList Choice { get; set; } 
} 

Pour le contrôleur:

// 
// GET: /Demo/Create 

public ActionResult Create() 
{ 
    DemoCreateViewModel data = new DemoCreateViewModel(); 
    data.Choice = new SelectList(new string[] { "Choice1", "Choice2", "Choice3" }); 
    ViewData.Model = data; 
    return View(); 
} 

// 
// POST: /Demo/Create 

[HttpPost] 
public ActionResult Create(DemoCreateViewModel form) 
{ 
    try 
    { 
     // TODO: Add insert logic here 

     return RedirectToAction("Index"); 
    } 
    catch 
    { 
     return View(); 
    } 
} 

Et pour la vue:

<fieldset> 
    <legend>Fields</legend> 
    <%= Html.LabelFor(model => model.Choice) %> 
    <%= Html.DropDownListFor(model => model.Choice, Model.Choice) %> 
    <p> 
     <input type="submit" value="Create" /> 
    </p> 
</fieldset> 

Maintenant, je sais que je peux FAITES ce travail en faisant une chute de 10 mètres et en donnant un coup de poing: contournez la reliure du modèle et retournez au FormCo llection et valider et lier tous les champs moi-même, mais il doit y avoir un moyen plus simple. Je veux dire, il s'agit d'une exigence aussi simple que possible. Y at-il un moyen de faire ce travail dans l'architecture MVC ModelBinding? Si oui, qu'est-ce que c'est? Et si non, comment ça se fait? Edit: Eh bien, j'ai un œuf sur le visage, mais peut-être que cela aidera quelqu'un d'autre. J'ai fait d'autres expériences et trouvé une solution simple qui semble fonctionner. Entrez une valeur simple (chaîne ou nombre entier, selon le type de votre valeur de liste de sélection) et nommez-le en tant qu'élément de modèle auquel vous vous liez.

Ensuite, fournissez un second élément en tant que liste de choix et nommez-le autrement. Donc, mon modèle est devenu:

public class DemoCreateViewModel 
{ 
    public string Choice { get; set; } 
    public SelectList Choices { get; set; } 
} 

Et puis la déclaration DropDownListFor dans la vue devient:

<%= Html.DropDownListFor(model => model.Choice, Model.Choices) %> 

Quand je fais cela, le bouton d'envoi se fixe correctement le choix sous la forme à la chaîne Choix, et soumet le modèle à la seconde méthode Create.

+0

Merci d'avoir publié votre correction, j'ai eu un problème similaire et votre modification a aidé – Chris

+0

Vous ne pouvez pas simplement le lier à un IEnumerable ou List of SelectListItems à la place? –

Répondre

5

Voici une approche:

@Html.DropDownListFor(model => model.Choice, 
         ViewBag.Choices as SelectList, 
         "-- Select an option--", 
         new { @class = "editor-textbox" }) 

Notez que j'utilise ViewBag pour contenir mon SelectList. De cette façon, lorsque vous publiez, le client n'envoie pas toute la liste de sélection au serveur dans le cadre du modèle.

Dans votre code de contrôleur, il vous suffit de mettre le sac de vue:

ViewBag.Choices = new SelectList(.... 
+1

* Notez que j'utilise ViewBag pour contenir ma SelectList. De cette façon, lorsque vous publiez, le client n'envoie pas toute la liste de sélection au serveur dans le cadre du modèle. * (1) Cela ne serait pas le cas; et (2) si c'était le cas, l'utilisation de ViewBag ne l'empêcherait pas d'être publiée, cela l'empêcherait simplement de se lier au modèle. –

0

Envisager la création d'un modèle de vue différent pour votre action post sans la propriété SelectList:

  public class DemoCreateViewModelForUpdate 
      { 
       public string Choice { get; set; } 
      } 

Ensuite, vous pouvez Toujours mapper de l'instance DemoCreateViewModelPost vers une instance DemoCreateViewModel si l'état du modèle n'est pas valide et que vous souhaitez afficher à nouveau la vue.J'ai tendance à préférer tout ce qui est requis par la vue pour être dans ma classe de modèle de vue d'affichage, donc en utilisant un modèle de vue de mise à jour séparé seulement, gardez les choses minces et coupées pour le retour au serveur.

À votre avis, vous feriez:

  @Html.DropDownListFor(m => m.Choice, Model.Choices) 

comme dans la réponse précédente, donc pas de données inutiles serait aller-retour.