Помощник по шаблону MVC - DropDown - PullRequest
3 голосов
/ 29 октября 2009

Используя шаблонных помощников в MVC2.0, я столкнулся с дилеммой, как получить элементы для заполнения выпадающего списка. Я использую атрибут [UIHint(BadgesDropDown)], но как мне получить элементы списка, не нарушая шаблон MVC, должен ли контроллер помещать их в ViewData? Должен ли BadgesDropDown.ascx вызвать помощника, чтобы получить их?

Прямо сейчас я иду за:

BadgesDropDown.ascx

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%= Html.DropDownList("", ViewData["Badges"] as IEnumerable<SelectListItem>)%>

Контроллер

ViewData["Badges"] = new SelectList(SiteRepository.GetBadges(), "RowKey", "BadgeName");

Это путь?

Ответы [ 3 ]

2 голосов
/ 29 июня 2010

В последнее время было много дискуссий на эту тему. Подобные препятствия встречаются с датами, диапазонами дат и списками множественных флажков. Везде, где вы можете использовать богатый набор HTML-элементов управления. Я экспериментировал с концепцией дочерних ViewModels и думаю, что решение чище, чем другие подходы, которые я пробовал.

Основная концепция заключается в том, что вы определяете модель малого представления, которая тесно связана с пользовательским шаблоном EditorTemplate.

В вашем примере мы начнем с (дочерней) ViewModel, специфичной для одного списка выбора:

public class SelectModel
{
  #region SelectModel(string value, IEnumerable<SelectListItem> items)
  public SelectModel(string value, IEnumerable<SelectListItem> items)
  {
    _value = value;
    Items = new List<SelectListItem>(items);

    _Select();
  } 
  #endregion

  // Properties

  public List<SelectListItem> Items { get; private set; }

  public string Value
  { 
    get { return _value; }
    set { _value = value; _Select();}
  }
  private string _value;

  // Methods

  private void _Select()
  {
    Items.ForEach(x => x.Selected = (Value != null && x.Value == Value));
  }
}

В модели представления, которая хочет использовать раскрывающийся список, вы составляете выбранную модель (мы все используем модели представления, верно?):

public class EmailModel
{
  // Constructors

  public EmailModel()
  {
    Priority = new SelectModel("normal", _ToPrioritySelectItems());
  }

  // Properties

  public SelectModel Priority { get; set; }

  // Methods

  private IEnumerable<SelectListItem> _ToPrioritySelectItems()
  {
    List<SelectListItem> result = new List<SelectListItem>();

    result.Add(new SelectListItem() { Text = "High", Value = "high" });
    ...
  }

Обратите внимание, что это простой пример с фиксированным набором выпадающих элементов. Если они поступают из уровня домена, контроллер передает их в ViewModel.

Затем добавьте шаблон редактора SelectModel.ascx в Shared / EditorTemplates

<%@ Control Inherits="System.Web.Mvc.ViewUserControl<SelectModel>" %>

<div class="set">
  <%= Html.LabelFor(model => model) %>
  <select id="<%= ViewData.ModelMetadata.PropertyName %>_Value" name="<%=ViewData.ModelMetadata.PropertyName %>.Value">
  <% foreach (var item in Model.Items) { %>
    <%= Html.OptionFor(item) %>
  <% } %>
  </select>
</div>

Примечание: OptionFor - это пользовательское расширение, которое делает очевидное

Хитрость в том, что идентификатор и имя задаются с использованием составного формата, который ожидает ModelBinder по умолчанию. В нашем примере «Приоритет. Значение». Таким образом, свойство Value на основе строки, определенное как часть SelectModel, устанавливается напрямую. Сеттер заботится об обновлении списка Предметов, чтобы установить опцию выбора по умолчанию, если нам нужно снова отобразить форму.

Там, где этот подход «модель дочернего представления» действительно сияет, более сложный «контрольный фрагмент разметки». Теперь у меня есть модели дочернего представления, которые следуют аналогичному подходу для списков MultiSelect, диапазонов дат начала / окончания и комбинаций Дата + время.

Как только вы идете по этому пути, следующий очевидный вопрос становится проверкой.

Я закончил тем, что все мои дочерние ViewModel реализовали стандартный интерфейс:

public interface IValidatable
{
  bool HasValue { get; }
  bool IsValid { get; }
}

Затем у меня есть собственный атрибут Validation:

public class IsValidAttribute : ValidationAttribute
{
  // Constructors

  public IsValidAttribute()
  {
    ErrorMessage = "(not valid)";
  }

  // Properties

  public bool IsRequired { get; set; }

  // Methods

  private bool Is(object value)
  {
    return value != null && !"".Equals(value);
  }

  public override bool IsValid(object value)
  {
    if (!Is(value) && !IsRequired)
      return true;

    if (!(value is IValidatable))
      throw new InvalidOperationException("IsValidAttribute requires underlying property to implement IValidatable");

    IValidatable validatable = value as IValidatable;
    return validatable.IsValid;
  }
}

Теперь вы можете просто поместить атрибуты в свойства, основанные на дочернем ViewModel, как любое скалярное свойство:

[IsValid(ErrorMessage = "Please enter a valid start date/time")]
public DateAndTimeModel Start { get; set; }
0 голосов
/ 09 ноября 2009

В MVC 2 отличный новый метод ... который, если он используется, опирается на все данные атрибута.

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" 
Inherits="System.Web.Mvc.ViewPage<glossaryDB.EntityClasses.AssociationEntity>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Association: Edit
</asp:Content>

<asp:Content ID="Content3" ContentPlaceHolderID="MainContent" runat="server">
    <h3>Association: Edit</h3>
    <% using (Html.BeginForm()) { %>
        <fieldset style="padding: 1em; margin: 0; border: solid 1px #999;">
            <%= Html.ValidationSummary("Edit was unsuccessful. Please correct the errors and try again.") %>
            <%= Html.EditorForModel() %>
            <input type="submit" value="  Submit  " />
        </fieldset>
    <% } %>
    <p><%= Html.ActionLink("Details", "Index") %></p>
</asp:Content>

Для этого есть 2 варианта. Либо UIHint должен предоставить источник данных, либо контроллер должен. Если UIHint делает, то данные, предоставленные для выпадающего списка, являются фиксированными. Другой вариант - контроллер, который позволяет нам переключать выпадающие данные с другим набором данных, как требуется.

Я нашел несколько похожих примеров:

Nerd Dinner

[1]: поиск для codeclimber.net.nz и инструкции по созданию выпадающего списка с помощью asp.net-mvc [2]: bradwilson.typepad.com и templates-part-5-master-page-templates

0 голосов
/ 06 ноября 2009

Я реализовал решение как приведенный выше пример. Следует отметить, что помощники должны работать только с предоставленными им данными, см. Просмотр зависимости

Лучше всего писать HTML помощники не знают контроллеров и контексты. Они должны делать свою работу только в зависимости от того, какие данные предоставляются звонящий.

Я согласен с вышеуказанным утверждением. Просто нужно проделать большую работу по сравнению с обычной разработкой ASP.Net.

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