ViewModel с привязкой SelectList в ASP.NET MVC2 - PullRequest
8 голосов
/ 25 февраля 2010

Я пытаюсь реализовать Edit ViewModel для моей сущности Linq2SQL под названием Product. У него есть внешний ключ, связанный со списком брендов.

В настоящее время я заполняю список брендов через 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, Name и Description отображаются правильно в сгенерированном представлении, но список выбора - нет. Список брендов определенно содержит данные.

Если я сделаю следующее в моем представлении, список выбора будет виден:

<% 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? Я пропускаю какую-то аннотацию данных?

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

Ответы [ 3 ]

4 голосов
/ 19 июня 2010

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-код, отображаемый в вашем представлении, уже указывает на метод действия «Изменить» с соответствующим «ProductId», как показано ниже.

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

Это ваш метод действия HTTP POST, и он принимает 2 параметра.
«Идентификатор» происходит из атрибута действия тега .

[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 для модификации данных раздел в этом сообщении в блоге Кази Манзур Рашид .
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 );

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

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

1 голос
/ 06 мая 2010

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

0 голосов
/ 20 марта 2010

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...