Pergunta

Usando os ajudantes templated em MVC2.0 Corri para um dillema, como obter os itens para preencher um dropdownlist. Eu estou usando um atributo [UIHint(BadgesDropDown)], mas como vou obter os itens da lista sem violar o padrão MVC, deve o lugar controlador-los no ViewData? Caso o BadgesDropDown.ascx invocar um ajudante para obtê-los?

Agora eu estou indo para:

BadgesDropDown.ascx

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

Controlador

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

É este o caminho a percorrer?

Foi útil?

Solução

No MVC 2 um ótimo método novo ... que se usado baseia-se em todos os dados de atributo.

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

Para que isso funcione, há 2 opções. Ou o UIHint tem de fornecer a fonte dos dados ou o mosto controlador. Se o UIHint acontecer, então os dados fornecidos para thhe suspensa é fixa. A outra opção é o controlador, o que nos permite mudar os dados suspensa com um conjunto diferente de dados como reqired.

Há alguns exemplos relacionados I encontrados:

Nerd Jantar

[1]: searcch para codeclimber.net.nz e how-to-create-a-dropdownlist-com-asp.net-mvc [2]: bradwilson.typepad.com e modelos-part-5-master-page-templates

Outras dicas

Tem havido muita discussão sobre este tópico recentemente. obstáculos semelhantes são encontrados com datas, períodos e multi-listas de seleção de caixa de seleção. Em qualquer lugar que você pode querer usar um rico conjunto de controles HTML. Estive a experimentar com o conceito de ViewModels criança e eu acho que a solução é mais limpo do que outras abordagens que eu tentei.

O conceito básico é que você define um pequeno modelo de vista que está estreitamente ligado a um EditorTemplate personalizado.

No seu exemplo, gostaríamos de começar com um (criança) ViewModel que é específico para uma única lista de seleção:

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

No modelo de vista que quer usar o menu suspenso a compor o modelo de seleção (todos nós estamos usando modelos de exibição, certo?):

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

Note que este é um exemplo simples com um conjunto fixo de itens suspensas. Se eles estão vindo a camada de domínio, o controlador passa-los para o ViewModel.

Em seguida, adicione um SelectModel.ascx template editor em Comum / 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>

Nota: OptionFor é uma extensão personalizada que faz o óbvio

O truque aqui é que o id eo nome são definidas utilizando o formato composto que os espera ModelBinder padrão. No nosso exemplo "Priority.Value". Assim, a propriedade valor da cadeia base que é definida como parte de SelectModel é definida diretamente. O setter cuida da actualização da lista de Itens para definir o selecione a opção padrão se precisamos voltar a exibir o formulário.

Onde esta abordagem "modelo de exibição de criança" realmente brilha é mais complexa "trechos de controle de marcação". Agora tenho vista criança modelos que seguem uma abordagem semelhante para listas MultiSelect, intervalos de datas Start / End, e combinações Data + tempo.

Assim que você ir por este caminho, a próxima pergunta óbvia se torna validação.

Acabei por ter toda a minha criança ViewModel de implementar uma interface padrão:

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

Então, eu tenho um costume ValidationAttribute:

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

Agora você pode apenas atributos colocar em propriedades que são ViewModel criança com base como qualquer propriedade escalar:

[IsValid(ErrorMessage = "Please enter a valid start date/time")]
public DateAndTimeModel Start { get; set; }

Eu implementou a solução como no exemplo acima. Uma coisa a se notar é que Helpers deve funcionar apenas com os dados fornecidos a eles, consulte Ver dependência

A melhor prática é escrever Html ajudantes desconhecem controladores e contextos. Eles devem fazer o seu trabalho apenas com base no que os dados são fornecidos pela o chamador.

Eu concordo com a afirmação acima. É que um monte de trabalho precisa de ser feito quando comparado com o desenvolvimento regular ASP.Net.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top