2010-02-25 17 views
8

Estoy tratando de implementar un Edit ViewModel para mi entidad Linq2SQL llamada Producto. Tiene una clave externa vinculada a una lista de marcas.ViewModel con enlace SelectList en ASP.NET MVC2

Actualmente estoy poblando la lista de marca a través de ViewData y el uso de DropDownListFor, por lo tanto:

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

Ahora quiero refactorizar el fin de utilizar un modelo de vista inflexible y Html.EditorForModel():

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

    <fieldset> 
     <legend>Fields</legend> 

     <%=Html.EditorForModel() %> 

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

<% } %> 

En mi modelo de vista Editar, Tengo el siguiente:

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; 
    } 
} 

el aire Troller se configura de este modo:

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); 
} 

El ProductId, Nombre y Descripción muestran correctamente en la vista generada, pero la lista de selección no lo hace. La lista de marcas definitivamente contiene datos.

Si hago lo siguiente, en mi opinión, la SelectList es 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é estoy haciendo mal? ¿EditorForModel() no admite genéricamente SelectList? ¿Me falta algo de DataAnnotation?

Parece que no puedo encontrar ejemplos del uso de SelectList en ViewModels que ayudan. Estoy realmente perplejo. This answer parece estar cerca, pero no ha ayudado.

+0

Parece que la próxima versión podría ser compatible con lo que estoy buscando: http://aspalliance.com/1687_ASPNET_MVC_Preview_3_Release.3 – Junto

Respuesta

4

Junto,

el método Html.EditorForModel() no es lo suficientemente inteligente como para que coincida con el BrandIdBrands lista de selección.

Primero, no puede usar el método abreviado EditorForModel().
Tienes que crear tu propia plantilla HTML así.

<% 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> 
<% } %> 



En segundo lugar, tiene que cambiar su método de acción.

[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); 
} 



En tercer lugar, es necesario poner al día su clase 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; 
    } 
} 

estas alturas, usted probablemente está diciendo: tipo, donde es mi [ProductID] propiedad "
respuesta corta:?. Usted no lo necesita

El HTML representado! por su vista ya apunta a método de acción "Editar" con un "ProductId" apropiado como se muestra a continuación.

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

Esta es su método de acción HTTP POST y acepta 2 parámetros
El "id" proviene del <forma> atributo de acción de la etiqueta.

[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 }); 
} 

El ExportModelStateToTempDataImportModelStateFromTempData y son muy útiles.
Esos atributos se utilizan para el patrón PRG (obtención posterior a la redirección).

Leer este patrón de uso para la modificación de datos PRG sección de este post del blog de Kazi Manzur Rashid.
http://weblogs.asp.net/rashid/archive/2009/04/01/asp-net-mvc-best-practices-part-1.aspx




bien, este código se unen datos no es mi forma favorita de hacer las cosas.

TryUpdateModel<Product>(p); 

Mi forma favorita de hacerlo es tener una separadainterface para el enlace de datos puros.

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 
{ 
} 

Y así es como actualizaré mi código de enlace de datos.

TryUpdateModel<IProductModel>(p); 

Lo que esto ayuda es que me facilita la vinculación de los datos de mis objetos modelo a partir de los datos posteriores a la publicación. Además, lo hace más seguro porque solo está vinculando los datos que desea vincular. Nada más y nada menos.

Avíseme si tiene alguna pregunta.

+0

Gracias Stun. La mejor respuesta de lejos. En resumen, EdutorForModel() no es lo suficientemente inteligente, y aún tengo que usar ViewData y Magic Strings para hacer que SelectList sea transparente. Tutorial muy útil. Gracias. – Junto

0

Debe compilar esa búsqueda EN su ViewModel. A continuación, cree un objeto Builder que cree ViewModel y llene esa búsqueda.

Después de todo, para eso es su ViewModel: para proporcionar un modelo específico para su vista.

1

un nuevo atributo DropDownList para su propiedad BrandId puede ser útil. Eche un vistazo al artículo Extending ASP.NET MVC 2 Templates. Pero este enfoque utiliza ViewData como fuente de elementos de la lista de selección.

Cuestiones relacionadas