2010-02-25 6 views
8

Я пытаюсь реализовать Edit ViewModel для моего объекта Linq2SQL под названием Product. Он имеет внешний ключ, связанный со списком брендов.ViewModel с привязкой SelectList в ASP.NET MVC2

В настоящее время я наполнение списка бренда через ViewData и используя DropDownListFor, таким образом:

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

Теперь я хочу, чтобы реорганизовать вид использовать сильно типизированные ViewModel и Html.EditorForModel():

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

    <fieldset> 
     <legend>Fields</legend> 

     <%=Html.EditorForModel() %> 

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

<% } %> 

В моем Edit ViewModel, я следующее:

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

Зэк Троллингист настроен так:

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

ProductID, Название и описание отображаются корректно в сгенерированном зрения, но список выбора не делает. Список брендов определенно содержит данные.

Если я следующий в моей точки зрения, SelectList видно:

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

<% } %> 

Что я делаю неправильно? Поддерживает ли EditorForModel() не общую поддержку SelectList? Я пропустил какой-то DataAnnotation?

Я не могу найти примеры использования SelectList в ViewModels, которые помогут. Я действительно в тупике. This answer кажется близким, но не помогло.

+0

Похоже, что следующая версия может поддерживать то, что я ищу: http://aspalliance.com/1687_ASPNET_MVC_Preview_3_Release.3 – Junto

ответ

4

Junto,

метод Html.EditorForModel() не достаточно умен, чтобы соответствовать BrandId с Brands списком выбора.

Во-первых, вы не можете использовать ярлык EditorForModel().
Вы должны создать свой собственный HTML-шаблон следующим образом.

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



Во-вторых, вы должны изменить способ действий.

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



В-третьих, вам нужно обновить 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; 
    } 
} 

К настоящему времени, вы, вероятно, говоря: Чувак, где моя [ProductID] свойство»
Короткий ответ:. Вам не нужно

HTML, оказывается! на ваш взгляд, уже указывает на «Edit» метод действия с соответствующим «ProductID», как показано ниже.

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

Это ваш метод действия HTTP POST и принимает 2 параметра.
«id» исходит из атрибута действия тега < >.

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

ExportModelStateToTempData и ImportModelStateFromTempData очень полезны.
Эти атрибуты используются для шаблона PRG (Post Redirect Get).

Прочитайте эту Используйте PRG шаблон для модификации данных раздела в этом блоге по Кази Manzur Рашид.
http://weblogs.asp.net/rashid/archive/2009/04/01/asp-net-mvc-best-practices-part-1.aspx




Хорошо, это привязка данных код не мой любимый способ делать вещи.

TryUpdateModel<Product>(p); 

Мой любимый способ сделать это, чтобы иметь отдельныйinterface для связывания чистых данных.

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

И вот как я буду обновлять код привязки данных.

TryUpdateModel<IProductModel>(p); 

Что это помогает, это упрощает передачу данных объектам модели из данных обратной связи. Кроме того, он делает его более безопасным, поскольку вы привязываете только данные, для которых вы хотите привязать. Ни больше ни меньше.

Дайте мне знать, если у вас есть вопросы.

+0

Спасибо Stun. Лучший ответ. Таким образом, EdutorForModel() недостаточно умен, и мне все равно придется использовать ViewData и магические строки, чтобы получить SelectList. Очень полезное прохождение. Спасибо. – Junto

0

Вы должны построить этот поиск в своей ViewModel. Затем создайте объект Builder, который создает ViewModel и заполняет этот поиск.

В конце концов, это то, что ваш ViewModel предназначен для: предоставления модели специально для вашего вида.

1

новый атрибут DropDownList для вашего имущества BrandId могут быть полезными. Посмотрите на статью Extending ASP.NET MVC 2 Templates. Но этот подход использует ViewData в качестве источника элементов selectlist.

Смежные вопросы