Pregunta

Usando los ayudantes de plantilla en MVC2.0 Me encontré con un dillema, cómo hacer que los elementos llenen una lista desplegable. Estoy usando un atributo [UIHint (BadgesDropDown)] , pero ¿cómo obtendré los elementos de la lista sin violar el Patrón MVC, si el controlador los coloca en ViewData? ¿Deberían las BadgesDropDown.ascx invocar a un Ayudante para obtenerlas?

Ahora mismo voy por:

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

¿Es este el camino a seguir?

¿Fue útil?

Solución

En el MVC 2, un gran método nuevo ... que, si se usa, depende de todos los datos de los atributos.

<%@ 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 esto funcione, hay 2 opciones. O UIHint tiene que proporcionar la fuente de los datos o el controlador debe hacerlo. Si el UIHint lo hace, entonces los datos proporcionados al menú desplegable son fijos. La otra opción es el controlador, que nos permite cambiar los datos desplegables con un conjunto diferente de datos según sea necesario.

Hay algunos ejemplos relacionados que encontré:

Cena Nerd

[1]: busca codeclimber.net.nz y cómo-crear-una-lista desplegable-con-asp.net-mvc   [2]: bradwilson.typepad.com y templates-part-5-master-page-templates

Otros consejos

Recientemente ha habido mucha discusión sobre este tema. Se encuentran obstáculos similares con fechas, rangos de fechas y listas de selección múltiple. En cualquier lugar que desee utilizar un amplio conjunto de controles html. He estado experimentando con el concepto de ViewModels infantiles y creo que la solución es más limpia que otros enfoques que he probado.

El concepto básico es que defina un modelo de vista pequeña que esté estrechamente relacionado con una plantilla de editor personalizada.

En su ejemplo, comenzaríamos con un ViewModel (secundario) específico para una lista de selección única:

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

En el modelo de vista que quiere usar el menú desplegable, compones el modelo seleccionado (todos usamos modelos de vista, ¿verdad?):

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

Tenga en cuenta que este es un ejemplo simple con un conjunto fijo de elementos desplegables. Si provienen de la capa de dominio, el controlador los pasa al ViewModel.

Luego agregue una plantilla de editor SelectModel.ascx en 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 es una extensión personalizada que hace lo obvio

El truco aquí es que la identificación y el nombre se configuran utilizando el formato compuesto que espera el ModelBinder predeterminado. En nuestro ejemplo " Priority.Value " ;. Por lo tanto, la propiedad Value basada en cadenas que se define como parte de SelectModel se establece directamente. El instalador se encarga de actualizar la lista de Elementos para establecer la opción de selección predeterminada si necesitamos volver a mostrar el formulario.

Donde este " modelo de vista secundaria " enfoque realmente brilla es más complejo "fragmentos de control de marcado". Ahora tengo modelos de vista secundaria que siguen un enfoque similar para las listas MultiSelect, los intervalos de fechas de inicio / finalización y las combinaciones de fecha y hora.

Tan pronto como siga este camino, la siguiente pregunta obvia se convierte en validación.

Terminé haciendo que todos los ViewModel de mi hijo implementaran una interfaz estándar:

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

Entonces, tengo un ValidationAttribute personalizado:

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

Ahora puede poner atributos en propiedades que son ViewModel secundario basado en cualquier propiedad escalar:

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

Implementé la solución como el ejemplo anterior. Una cosa a tener en cuenta es que los Ayudantes solo deben trabajar con los datos que se les proporcionan, consulte Ver dependencia

  

La mejor práctica es escribir HTML   ayudantes que desconocen los controladores y   contextos Deberían hacer su trabajo   solo en función de los datos proporcionados por   la persona que llama.

Estoy de acuerdo con la declaración anterior. Es solo que hay mucho trabajo por hacer en comparación con el desarrollo normal de ASP.Net.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top