2010-02-25 4 views
8

J'essaie d'implémenter un Edit ViewModel pour mon entité Linq2SQL appelée Product. Il a une clé étrangère liée à une liste de marques.ViewModel avec liaison SelectList dans ASP.NET MVC2

Actuellement, je suis peuplait la liste des marques via ViewData et en utilisant DropDownListFor, ainsi:

<div class="editor-field"> 
    <%= Html.DropDownListFor(model => model.BrandId, (SelectList)ViewData["Brands"])%> 
    <%= Html.ValidationMessageFor(model => model.BrandId) %> 
</div> 

Maintenant, je veux factoriser la vue d'utiliser un ViewModel et Html.EditorForModel() fortement typé:

<% using (Html.BeginForm()) {%> 
    <%= Html.ValidationSummary(true) %> 

    <fieldset> 
     <legend>Fields</legend> 

     <%=Html.EditorForModel() %> 

     <p> 
      <input type="submit" value="Save" /> 
     </p> 
    </fieldset> 

<% } %> 

Dans mon Modifier ViewModel, je donne les résultats suivants:

public class EditProductViewModel 
{ 
    [HiddenInput] 
    public int ProductId { get; set; } 

    [Required()] 
    [StringLength(200)] 
    public string Name { get; set; } 

    [Required()] 
    [DataType(DataType.Html)] 
    public string Description { get; set; } 

    public IEnumerable<SelectListItem> Brands { get; set; } 

    public int BrandId { get; set; } 

    public EditProductViewModel(Product product, IEnumerable<SelectListItem> brands) 
    { 
     this.ProductId = product.ProductId; 
     this.Name = product.Name; 
     this.Description = product.Description; 
     this.Brands = brands; 
     this.BrandId = product.BrandId; 
    } 
} 

le con Troller est configuré comme ceci:

public ActionResult Edit(int id) 
{ 
    BrandRepository br = new BrandRepository(); 

    Product p = _ProductRepository.Get(id); 
    IEnumerable<SelectListItem> brands = br.GetAll().ToList().ToSelectListItems(p.BrandId); 

    EditProductViewModel model = new EditProductViewModel(p, brands); 

    return View("Edit", model); 
} 

Le ProductId, le nom et l'affichage Description correctement dans la vue générée, mais la liste de sélection ne fonctionne pas. La liste des marques contient définitivement des données.

Si je fais ce qui suit à mon avis, le SelectList est visible:

<% using (Html.BeginForm()) {%> 
    <%= Html.ValidationSummary(true) %> 

    <fieldset> 
     <legend>Fields</legend> 

     <%=Html.EditorForModel() %> 

     <div class="editor-label"> 
      <%= Html.LabelFor(model => model.BrandId) %> 
     </div> 
     <div class="editor-field"> 
      <%= Html.DropDownListFor(model => model.BrandId, Model.Brands)%> 
      <%= Html.ValidationMessageFor(model => model.BrandId) %> 
     </div> 
     <p> 
      <input type="submit" value="Save" /> 
     </p> 
    </fieldset> 

<% } %> 

Qu'est-ce que je fais mal? Est-ce que EditorForModel() ne supporte pas génériquement la SelectList? Ai-je manqué une sorte de DataAnnotation?

Je n'arrive pas à trouver d'exemples d'utilisation de SelectList dans ViewModels. Je suis vraiment perplexe. This answer semble être proche, mais n'a pas aidé.

+0

On dirait que la prochaine version pourrait soutenir ce que je cherche: http://aspalliance.com/1687_ASPNET_MVC_Preview_3_Release.3 – Junto

Répondre

4

Junto,

la méthode Html.EditorForModel() est pas assez intelligent pour correspondre BrandId avec la liste de sélection Brands.

D'abord, vous ne pouvez pas utiliser la méthode du raccourci EditorForModel().
Vous devez créer votre propre template HTML de la sorte.

<% using (Html.BeginForm()) { %> 

    <div style="display:none"><%= Html.AntiForgeryToken() %></div> 

    <table> 
     <tr> 
      <td><%= Html.LabelFor(m => m.Name) %></td> 
      <td><%= Html.EditorFor(m => m.Name) %></td> 
     </tr> 

     <tr> 
      <td><%= Html.LabelFor(m => m.Description) %></td> 
      <td><%= Html.EditorFor(m => m.Description) %></td> 
     </tr> 

     <tr> 
      <td><%= Html.LabelFor(m => m.BrandId) %></td> 
      <td><%= Html.EditorFor(m => m.BrandId) %></td> 
     </tr> 
    </table> 
<% } %> 



Deuxièmement, vous devez changer votre méthode d'action.

[ImportModelStateFromTempData] 
public ActionResult Edit(int id) 
{ 
    BrandRepository br = new BrandRepository(); 

    Product p = _ProductRepository.Get(id); 
    ViewData["BrandId"] = br.GetAll().ToList().ToSelectListItems(p.BrandId); 

    EditProductViewModel model = new EditProductViewModel(p); 

    return View("Edit", model); 
} 



Troisièmement, vous devez mettre à jour votre classe EditProductViewModel.

public class EditProductViewModel 
{ 
    [Required] 
    [StringLength(200)] 
    public string Name { get; set; } 

    [Required()] 
    [DataType(DataType.Html)] 
    public string Description { get; set; } 

    [Required] // this foreign key *should* be required 
    public int BrandId { get; set; } 

    public EditProductViewModel(Product product) 
    { 
     this.Name = product.Name; 
     this.Description = product.Description; 
     this.BrandId = product.BrandId; 
    } 
} 

Maintenant, vous êtes probablement dit: Mec, où est mon [ProductID] propriété »
Réponse courte:. Vous ne avez pas besoin

Le rendu HTML! par votre point de vue déjà fait remarquer méthode d'action « Edition » avec une appropriée « ProductID » comme indiqué ci-dessous.

<form action="/Product/Edit/123" method="post"> 
    ... 
</form> 

Ceci est votre méthode d'action HTTP POST et accepte 2 paramètres.
Le "id" provient de l'attribut d'action <forme>.

[HttpPost, ValidateAntiForgeryToken, ExportModelStateToTempData] 
public ActionResult Edit(int id, EditProductViewModel model) 
{ 
    Product p = _ProductRepository.Get(id); 

    // make sure the product exists 
    // otherwise **redirect** to [NotFound] view because this is a HTTP POST method 
    if (p == null) 
     return RedirectToAction("NotFound", new { id = id }); 

    if (ModelState.IsValid) 
    { 
     TryUpdateModel<Product>(p); 
     _ProductRepository.UpdateProduct(p); 
    } 

    return RedirectToAction("Edit", new { id = id }); 
} 

Le ExportModelStateToTempData et ImportModelStateFromTempData sont très utiles.
Ces attributs sont utilisés pour le modèle PRG (Post Redirect Get).

Lire ce Utilisez PRG modèle pour la section Modification des données dans ce blog par Kazi Manzur Rashid.
http://weblogs.asp.net/rashid/archive/2009/04/01/asp-net-mvc-best-practices-part-1.aspx




D'accord, ce code de liaison de données ne sont pas ma façon préférée de faire les choses.

TryUpdateModel<Product>(p); 

Ma façon préférée de le faire est d'avoir un séparéinterface pour les données pures de liaison.

public interface IProductModel 
{ 
    public string Name {get; set;} 
    public string Description {get; set;} 
    public int BrandId {get; set;} 
} 

public partial class Product : IProductModel 
{ 
} 

public partial class EditProductViewModel : IProductModel 
{ 
} 

Et voilà comment je vais mettre à jour mes données code de liaison.

TryUpdateModel<IProductModel>(p); 

Ce qui aide cela est qu'il me soit facile de lier mes objets de modèle à partir de données postérieures. En outre, cela le rend plus sûr car vous ne faites que lier les données pour lesquelles vous voulez lier. Ni plus ni moins. Informez-moi si vous avez des questions.

+0

Thanks Stun. Meilleure réponse de loin. En résumé, EdutorForModel() n'est pas assez intelligent, et je dois encore utiliser ViewData et les chaînes magiques pour faire passer la SelectList. Walkthrough très utile. Je vous remercie. – Junto

0

Vous devez créer cette recherche dans votre ViewModel. Créez ensuite un objet Builder qui crée le ViewModel et remplit cette recherche.

Après tout, c'est ce que votre ViewModel est pour: fournir un modèle spécifique pour votre vue.

1

Un nouvel attribut DropDownList pour votre propriété BrandId peut vous être utile. Jetez un oeil à l'article Extending ASP.NET MVC 2 Templates. Mais cette approche utilise ViewData comme source d'éléments de liste de sélection.