2009-10-29 8 views
3

En utilisant les assistants basés sur des modèles dans MVC2.0, j'ai rencontré un dillema, comment obtenir les éléments pour remplir une liste déroulante. J'utilise un attribut [UIHint(BadgesDropDown)], mais comment vais-je obtenir les éléments de la liste sans violer le modèle MVC, si le contrôleur les place dans la ViewData? Est-ce que le BadgesDropDown.ascx invoque un assistant pour les obtenir?MVC Templated Helper - DropDown

En ce moment je vais faire:

BadgesDropDown.ascx

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

Contrôleur

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

Est-ce la voie à suivre?

Répondre

0

Dans le MVC 2, une nouvelle méthode ... qui, si elle est utilisée, repose sur toutes les données d'attribut.

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

Pour que cela fonctionne il y a 2 options. UIHint doit fournir la source des données ou le contrôleur doit. Si UIHint le fait, les données fournies dans la liste déroulante sont corrigées. L'autre option est le contrôleur, ce qui nous permet de remplacer les données du menu déroulant par un ensemble de données différent.

Il y a quelques exemples connexes que j'ai trouvé:

Dîner Nerd

[1]: searcch pour codeclimber.net.nz et comment à créer-un-dropdownlist-avec-asp.net- mvc [2]: bradwilson.typepad.com et modèles-partie-5-maîtres-page-modèles

-1

J'ai implémenté la solution comme dans l'exemple ci-dessus. Une chose à noter est que Helpers ne doit travailler avec les données qui leur sont fournies, voir View dependency

La meilleure pratique consiste à écrire des aides Html pas au courant des contrôleurs et contextes. Ils devraient faire leur travail seulement basé sur quelles données sont fournies par l'appelant.

Je suis d'accord sur l'énoncé ci-dessus. C'est juste que beaucoup de travail doit être fait par rapport au développement ASP.Net régulier.

2

Il y a eu beaucoup de discussions sur ce sujet récemment. Des barrages routiers similaires sont rencontrés avec des dates, des plages de dates et des listes de cases à cocher à sélection multiple. Partout où vous voudrez utiliser un riche ensemble de contrôles html. J'ai expérimenté avec le concept de ViewModels enfant et je pense que la solution est plus propre que d'autres approches que j'ai essayées.

Le concept de base est que vous définissiez un petit modèle de vue étroitement associé à un EditorTemplate personnalisé.

Dans votre exemple, nous commencerions avec un (enfant) ViewModel qui est spécifique à une seule liste de sélection:

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

Dans le modèle de vue qui veut utiliser le menu déroulant vous rédigez le modèle de sélection (nous » utilisez-vous tous les modèles de vue, n'est-ce pas?):

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

Notez qu'il s'agit d'un exemple simple avec un ensemble fixe d'éléments déroulants. Si elles proviennent de la couche de domaine, le contrôleur les passe dans le ViewModel.

Puis ajouter un modèle d'éditeur SelectModel.ascx dans 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> 

Note: OptionFor est une extension personnalisée qui fait l'évidence

L'astuce ici est que l'identifiant et le nom sont définis à l'aide le format composé attendu par ModelBinder par défaut. Dans notre exemple "Priority.Value". Ainsi, la propriété Value basée sur une chaîne définie dans le cadre de SelectModel est définie directement. Le régleur prend soin de mettre à jour la liste des éléments pour définir l'option de sélection par défaut si nous devons réafficher le formulaire. Lorsque cette approche "child view model" brille vraiment, c'est plus complexe "des extraits de contrôle de balisage". J'ai maintenant des modèles de vue enfant qui suivent une approche similaire pour les listes MultiSelect, les plages de dates de début/fin et les combinaisons date/heure.

Dès que vous suivez ce chemin, la prochaine question évidente devient la validation.

J'ai fini tout mon enfant mettre en œuvre une interface standard de ViewModel:

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

Ensuite, j'ai une coutume 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; 
    } 
} 

Maintenant, vous pouvez simplement mettre des attributs sur les propriétés qui sont enfant ViewModel basé comme toute propriété scalaire:

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