2012-06-14 35 views
16

Estoy usando ASP.NET MVC 3, y acabo de ejecutar un 'gotcha' usando el DropDownListFor HTML Helper.ASP.NET MVC DropDownListPara no seleccionar el valor del modelo

hago esto en mi controlador:

ViewBag.ShippingTypes = this.SelectListDataRepository.GetShippingTypes(); 

Y el GetShippingTypes método:

public SelectList GetShippingTypes() 
{ 
    List<ShippingTypeDto> shippingTypes = this._orderService.GetShippingTypes(); 

    return new SelectList(shippingTypes, "Id", "Name"); 
} 

La razón por la que puse en el ViewBag y no en el modelo (fuertemente He tecleado modelos para cada vista), es que tengo una colección de elementos que se renderiza usando un EditorTemplate, que también necesita acceder a la lista de selección ShippingTypes.

De lo contrario, tengo que recorrer toda la colección y asignarle una propiedad ShippingTypes.

Hasta ahora todo bien.

En mi opinión, hago esto:

@Html.DropDownListFor(m => m.RequiredShippingTypeId, ViewBag.ShippingTypes as SelectList) 

(RequiredShippingTypeId es de tipo Int32)

Lo que sucede es, que el valor de RequiredShippingTypeId es no seleccionado en el menú desplegable.

me encontré con esto: http://web.archive.org/web/20090628135923/http://blog.benhartonline.com/post/2008/11/24/ASPNET-MVC-SelectList-selectedValue-Gotcha.aspx

Sugiere que MVC buscar el valor seleccionado de ViewData, cuando la lista de selección es de ViewData. No estoy seguro de que este sea el caso más, ya que la publicación del blog es antigua y está hablando de MVC 1 beta.

Una solución que resuelve este problema es la siguiente:

@Html.DropDownListFor(m => m.RequiredShippingTypeId, new SelectList(ViewBag.ShippingTypes as IEnumerable<SelectListItem>, "Value", "Text", Model.RequiredShippingTypeId.ToString())) 

no trataron de ToString en RequiredShippingTypeId al final, lo que me da el mismo comportamiento que antes: No hay elemento seleccionado.

Estoy pensando que esto es un problema de tipo de datos. En última instancia, el helper de HTML está comparando cadenas (en la lista de selección) con el Int32 (del RequiredShippingTypeId).

Pero, ¿por qué no funciona al poner el SelectList en el ViewBag - cuando funciona perfectamente cuando se añade a un modelo, y haciendo esto dentro de la vista:

@Html.DropDownListFor(m => m.Product.RequiredShippingTypeId, Model.ShippingTypes) 
+0

Gracias por la solución! Así que no es obvio que la magia solo funciona con expresiones lambda "simples", y el sistema no advierte al respecto. –

Respuesta

30

La razón por la que esto no funciona es debido a una limitación de la DropDownListFor ayudante: es capaz de deducir el valor seleccionado utilizando la expresión lambda pasa como primer argumento sólo si esta expresión lambda es una expresión simple de acceso a la propiedad. Por ejemplo, esto no funciona con las expresiones de acceso del indexador de matriz, que es su caso debido a la plantilla del editor.

que básicamente tienen (excluyendo el editor de plantillas): No se admite

@Html.DropDownListFor(
    m => m.ShippingTypes[i].RequiredShippingTypeId, 
    ViewBag.ShippingTypes as IEnumerable<SelectListItem> 
) 

la siguiente: m => m.ShippingTypes[i].RequiredShippingTypeId. Funciona solo con expresiones de acceso a propiedades simples pero no con acceso a colecciones indexadas.

La solución que ha encontrado es la forma correcta de resolver este problema, al pasar explícitamente el valor seleccionado al generar el SelectList.

+0

Maldita sea, no sabía eso. Puedo confirmar que este es realmente el caso. Traté de usar el 'ShippingTypes' del' ViewBag', en lugar del modelo para el menú desplegable que toma el tipo de envío general: '@ Html.DropDownListFor (m => m.Product.RequiredShippingTypeId, ViewBag.ShippingTypes as SelectList) '- y eso funciona. Por lo tanto, supongo que tengo que seguir con la fea solución, podría escribir una ayuda HTML para eso, en lugar de dejar trucos sucios en mis puntos de vista. – MartinHN

+0

@Darin - ¿me puede indicar la documentación para esto? Gracias. – mokumaxCraig

+0

Esto parece aplicarse también si está utilizando algún otro objeto/alias para obtener el valor de p. Ej. '@ Html.DropDownListFor (m => myViewModel.RequiredShippingTypeID, ...'. ¡Gracias! – xr280xr

0

Esto podría ser tonto, pero ¿Agregar algo a una variable en su vista hace algo?

var shippingTypes = ViewBag.ShippingTypes; 

@Html.DropDownListFor(m => m.Product.RequiredShippingTypeId, shippingTypes) 
+0

No, esto no resolverá el problema. –

+0

Nunca lo intenté, pero estoy seguro de que @DarinDimitrov tiene razón. – MartinHN

+0

k genial solo pensé que lo sugeriría como una posible solución "limpia". – Terry

0

Hay un método sobrecargado para @ html.DropdownList para manejar esto. Hay una alternativa para establecer el valor seleccionado en la lista desplegable de HTML.

@Html.DropDownListFor(m => m.Section[b].State, 
       new SelectList(Model.StatesDropdown, "value", "text", Model.Section[b].State)) 

Pude obtener el valor seleccionado del modelo.

"value", "text", Model.Section[b].State esta sección la sintaxis anterior añade el atributo seleccionado al valor cargado desde el controlador

0

puede crear viewdata dinámico en lugar de viewbag para cada campo DropDownList para el tipo complejo. esperanza esto le dará pista de cómo hacer que

 @if (Model.Exchange != null) 
 
           { 
 
            for (int i = 0; i < Model.Exchange.Count; i++) 
 
            { 
 
             <tr> 
 
              @Html.HiddenFor(model => model.Exchange[i].companyExchangeDtlsId) 
 
              <td> 
 
                
 
               @Html.DropDownListFor(model => model.Exchange[i].categoryDetailsId, ViewData["Exchange" + i] as SelectList, " Select category", new { @id = "ddlexchange", @class = "form-control custom-form-control required" }) 
 
               @Html.ValidationMessageFor(model => model.Exchange[i].categoryDetailsId, "", new { @class = "text-danger" }) 
 
              </td> 
 
              <td> 
 
               @Html.TextAreaFor(model => model.Exchange[i].Address, new { @class = "form-control custom-form-control", @style = "margin:5px;display:inline" }) 
 
              
 
               @Html.ValidationMessageFor(model => model.Exchange[i].Address, "", new { @class = "text-danger" }) 
 
              </td> 
 
             </tr> 
 
            } 
 

 
           }

ViewModel CompanyDetail = companyDetailService.GetCompanyDetails(id); 
 
       if (CompanyDetail.Exchange != null) 
 
       for (int i = 0; i < CompanyDetail.Exchange.Count; i++) 
 
       { 
 
        ViewData["Exchange" + i]= new SelectList(companyDetailService.GetComapnyExchange(), "categoryDetailsId", "LOV", CompanyDetail.Exchange[i].categoryDetailsId); 
 
       }

0

Estaba golpeado por esta limitación y cuenta de una solución simple. El método de extensión que se acaba de definir genera internamente SelectList con el elemento seleccionado correcto.

public static class HtmlHelperExtensions 
{ 
    public static MvcHtmlString DropDownListForEx<TModel, TProperty>(
     this HtmlHelper<TModel> htmlHelper, 
     Expression<Func<TModel, TProperty>> expression, 
     IEnumerable<SelectListItem> selectList, 
     object htmlAttributes = null) 
    { 
     var selectedValue = expression.Compile().Invoke(htmlHelper.ViewData.Model); 
     var selectListCopy = new SelectList(selectList.ToList(), nameof(SelectListItem.Value), nameof(SelectListItem.Text), selectedValue); 

     return htmlHelper.DropDownListFor(expression, selectListCopy, htmlAttributes); 
    } 
} 

Lo mejor es que esta extensión se puede utilizar de la misma manera que el original DropDownListFor:

@for(var i = 0; i < Model.Items.Count(); i++) 
{ 
    @Html.DropDownListForEx(x => x.Items[i].CountryId, Model.AllCountries) 
} 
Cuestiones relacionadas