Domanda

Usando gli helper basati su modelli in MVC2.0 mi sono imbattuto in un dillema, come ottenere gli elementi per riempire un elenco a discesa. Sto usando un attributo [UIHint (BadgesDropDown)] , ma come posso ottenere gli elementi dell'elenco senza violare il modello MVC, il controller dovrebbe posizionarli in ViewData? BadgesDropDown.ascx dovrebbe invocare un aiutante per ottenerli?

In questo momento vado per:

BadgesDropDown.ascx

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

Regolatore

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

È questa la strada da percorrere?

È stato utile?

Soluzione

In MVC 2 un nuovo fantastico metodo ... che se usato si basa su tutti i dati degli attributi.

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

Perché questo funzioni ci sono 2 opzioni. OIHint deve fornire l'origine dei dati o il controller deve. Se UIHint lo fa, i dati forniti al menu a discesa vengono corretti. L'altra opzione è il controller, che ci consente di scambiare i dati a discesa con un diverso set di dati come richiesto.

Ci sono alcuni esempi correlati che ho trovato:

Cena da nerd

[1]: ricerca per codeclimber.net.nz e how-to-create-a-dropdownlist-with-asp.net-mvc   [2]: bradwilson.typepad.com e modelli-parte-5-pagina-principale-modelli

Altri suggerimenti

Di recente si è discusso molto su questo argomento. Blocchi stradali simili si incontrano con date, intervalli di date ed elenchi di caselle di selezione multipla. Ovunque potresti voler usare un ricco set di controlli html. Ho sperimentato il concetto di ViewModels figlio e penso che la soluzione sia più pulita di altri approcci che ho provato.

Il concetto di base è che si definisce un modello di vista piccolo che è strettamente accoppiato a un EditorTemplate personalizzato.

Nel tuo esempio, inizieremmo con un ViewModel (figlio) specifico per un singolo elenco di selezione:

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

Nel modello di visualizzazione che desidera utilizzare il menu a discesa, componi il modello selezionato (stiamo tutti utilizzando i modelli di visualizzazione, giusto?):

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

Nota che questo è un semplice esempio con un set fisso di elementi a discesa. Se provengono dal livello di dominio, il controller li passa nel ViewModel.

Quindi aggiungi un modello di editor SelectModel.ascx in 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>

Nota: OptionFor è un'estensione personalizzata che fa l'ovvio

Il trucco qui è che l'id e il nome sono impostati usando il formato composto che ModelBinder predefinito si aspetta. Nel nostro esempio " Priority.Value " ;. Pertanto, la proprietà Value basata su stringa definita come parte di SelectModel viene impostata direttamente. Il setter si occupa di aggiornare l'elenco degli elementi per impostare l'opzione di selezione predefinita se è necessario visualizzare nuovamente il modulo.

Dove questo "modello di visualizzazione figlio" l'approccio brilla davvero è più complesso "snippet di controllo di markup". Ora ho modelli di visualizzazione figlio che seguono un approccio simile per elenchi MultiSelect, intervalli di date di inizio / fine e combinazioni di data + ora.

Non appena percorri questa strada, la successiva domanda ovvia diventa validazione.

Ho finito per avere tutti i ViewModel di mio figlio implementare un'interfaccia standard:

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

Quindi, ho un ValidationAttribute personalizzato:

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

Ora puoi semplicemente mettere gli attributi su proprietà che sono ViewModel figlio come qualsiasi proprietà scalare:

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

Ho implementato la soluzione come nell'esempio sopra. Una cosa da notare è che gli helper dovrebbero lavorare solo con i dati forniti, vedere Visualizza dipendenza

  

La migliore pratica è scrivere HTML   aiutanti ignari dei controller e   contesti. Dovrebbero fare il loro lavoro   basato solo sui dati forniti da   il chiamante.

Sono d'accordo sulla dichiarazione di cui sopra. È solo che bisogna fare molto lavoro rispetto al normale sviluppo ASP.Net.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top