2012-06-14 13 views
16

Sto utilizzando ASP.NET MVC 3 e ho appena eseguito un 'gotcha' utilizzando l'helper HTML DropDownListFor.ASP.NET MVC DropDownListPer non selezionare il valore dal modello

Lo faccio nel mio controller:

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

e il metodo GetShippingTypes:

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

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

La ragione per cui ho messo nel ViewBag e non nel modello (che ho fortemente tipizzato modelli per ogni vista), è che ho una raccolta di elementi che esegue il rendering utilizzando un EditorTemplate, che deve anche accedere all'elenco di selezione ShippingTypes.

In caso contrario, ho bisogno di scorrere l'intera raccolta e assegnare quindi una proprietà ShippingTypes.

Fin qui tutto bene.

A mio avviso, faccio questo:

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

(RequiredShippingTypeId è di tipo Int32)

ciò che accade è che il valore di RequiredShippingTypeId è non selezionato nel menu a discesa.

mi sono imbattuto in questo: http://web.archive.org/web/20090628135923/http://blog.benhartonline.com/post/2008/11/24/ASPNET-MVC-SelectList-selectedValue-Gotcha.aspx

Egli suggerisce che MVC si ricerca il valore selezionato da ViewData, quando la lista di selezione è da ViewData. Non sono più sicuro che sia il caso, dal momento che il post sul blog è vecchio e parla di MVC 1 beta.

Una soluzione che risolve questo problema è questo:

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

ho cercato di non ToString sul RequiredShippingTypeId alla fine, che mi dà lo stesso comportamento di prima: Nessun elemento selezionato.

Sto pensando che questo è un problema di tipo di dati. In definitiva, l'helper HTML sta confrontando le stringhe (nell'elenco Seleziona) con lo Int32 (dallo RequiredShippingTypeId).

Ma perché non funziona quando mettere il SelectList nel ViewBag - quando funziona perfettamente quando si aggiunge ad un modello, e facendo questo all'interno della vista:

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

Grazie per la soluzione! Quindi non ovvio che la magia funzioni solo con espressioni "semplici" lambda, e il sistema non dà alcun avvertimento a riguardo. –

risposta

30

Il motivo per cui questo non funziona a causa di una limitazione della DropDownListFor helper: è in grado di dedurre il valore selezionato utilizzando l'espressione lambda passata come primo argomento solo se questa espressione lambda è un espressione di accesso alla proprietà semplice. Ad esempio questo non funziona con le espressioni di accesso dell'indicizzatore di array, che è il tuo caso a causa del modello di editor.

Hai sostanzialmente (escludendo l'editor di modelli):

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

Quanto segue non è supportato: m => m.ShippingTypes[i].RequiredShippingTypeId. Funziona solo con semplici espressioni di accesso alle proprietà ma non con accesso di raccolta indicizzato.

La soluzione trovata è il modo corretto per risolvere questo problema, passando esplicitamente il valore selezionato quando si crea lo SelectList.

+0

Dannazione, non lo sapevo. Posso confermare che questo è davvero il caso. Ho provato a utilizzare 'ShippingTypes' dal' ViewBag', invece del modello per il menu a discesa che utilizza il tipo di spedizione generale: '@ Html.DropDownListFor (m => m.Product.RequiredShippingTypeId, ViewBag.ShippingTypes come SelectList) '- e questo funziona. Quindi, credo che dovrò attenermi alla brutta soluzione, scrivere un helper HTML per questo, invece di lasciare degli hack sporchi nei miei punti di vista. – MartinHN

+0

@Darin - puoi indicarmi la documentazione per questo? Grazie. – mokumaxCraig

+0

Questo sembra essere valido anche se stai usando qualche altro oggetto/alias per ottenere il valore da es. '@ Html.DropDownListFor (m => myViewModel.RequiredShippingTypeID, ...'. Grazie! – xr280xr

0

Questo potrebbe essere stupido, ma lo aggiunge ad una variabile nella tua vista, fai qualcosa?

var shippingTypes = ViewBag.ShippingTypes; 

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

No, questo non risolverà il problema. –

+0

Non l'ho mai provato, ma sono sicuro che @DarinDimitrov ha ragione. – MartinHN

+0

k cool ho pensato di suggerirlo come una possibile soluzione "più pulita". – Terry

0

C'è un metodo sovraccarico per @ html.DropdownList per gestirlo. C'è un'alternativa per impostare il valore selezionato sull'elenco a discesa HTML.

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

Sono stato in grado di ottenere il valore selezionato dal modello.

"value", "text", Model.Section[b].State questa sezione la sintassi sopra aggiunge l'attributo selezionato il valore caricato dal controllore

0

è possibile creare viewdata dinamico anziché viewbag per ogni campo dropdownlist per tipo complesso. speranza questo vi darà suggerimento come fare

 @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

sono stato appena colpito da questa limitazione e capito una soluzione semplice. Appena definito il metodo di estensione che genera internamente SelectList con l'elemento selezionato corretto.

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

La cosa migliore è che questa estensione può essere utilizzato allo stesso modo come originale DropDownListFor:

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