MVC Templated Helper - DropDown
-
10-07-2019 - |
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?
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.