Jeśli uważamy, że nie jest to błąd, który zespół powinien naprawić, przynajmniej MSDN powinno poprawić dokument. Zagmatwanie naprawdę pochodzi z kiepskiego dokumentu tego. W MSDN wyjaśnia nazwę parametrów jako,
Type: System.String
The name of the form field to return.
Oznacza to po prostu, że ostateczny wygenerowany kod HTML będzie używał tego parametru jako nazwy wybranego wejścia. Ale to w rzeczywistości oznacza coś więcej.
Myślę, że projektant zakłada, że użytkownik użyje modelu widoku do wyświetlenia listy rozwijanej, a także użyje postu z powrotem do tego samego modelu widoku. Ale w wielu przypadkach tak naprawdę nie kierujemy się tym założeniem.
Skorzystaj z powyższego przykładu,
public class Person {
public int Id { get; set; }
public string Name { get; set; }
}
Jeśli będziemy postępować zgodnie z założeniem, powinniśmy zdefiniować model widoku dla tego widoku powiązanego z rozwijaną listą
public class PersonsSelectViewModel{
public string SelectedPersonId,
public List<SelectListItem> Persons;
}
Ponieważ podczas wysyłania zwrotnego tylko wybrana wartość zostanie odesłana, więc przyjmuje się, że powinna zostać wysłana z powrotem do właściwości modelu SelectedPersonId, co oznacza, że pierwsza nazwa parametru Html.DropDownList powinna mieć wartość „SelectedPersonId”. Dlatego projektant uważa, że podczas wyświetlania widoku modelu w widoku właściwość modelu SelectedPersonId powinna zawierać wartość domyślną tej listy rozwijanej. Nawet jeśli Twoja lista <SelectListItem> Persons już ustawiła flagę Selected, aby wskazać, która z nich jest wybrana / default, tml.DropDownList faktycznie zignoruje to i przebuduje swój własny IEnumerable <SelectListItem> i ustawi domyślny / wybrany element na podstawie nazwy.
Oto kod z asp.net mvc
private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMetadata metadata,
string optionLabel, string name, IEnumerable<SelectListItem> selectList, bool allowMultiple,
IDictionary<string, object> htmlAttributes)
{
...
bool usedViewData = false;
// If we got a null selectList, try to use ViewData to get the list of items.
if (selectList == null)
{
selectList = htmlHelper.GetSelectData(name);
usedViewData = true;
}
object defaultValue = (allowMultiple) ? htmlHelper.GetModelStateValue(fullName, typeof(string[])) : htmlHelper.GetModelStateValue(fullName, typeof(string));
// If we haven't already used ViewData to get the entire list of items then we need to
// use the ViewData-supplied value before using the parameter-supplied value.
if (defaultValue == null && !String.IsNullOrEmpty(name))
{
if (!usedViewData)
{
defaultValue = htmlHelper.ViewData.Eval(name);
}
else if (metadata != null)
{
defaultValue = metadata.Model;
}
}
if (defaultValue != null)
{
selectList = GetSelectListWithDefaultValue(selectList, defaultValue, allowMultiple);
}
...
return tagBuilder.ToMvcHtmlString(TagRenderMode.Normal);
}
Tak więc kod poszedł dalej, nie tylko spróbuje wyszukać nazwę w modelu, ale także w danych viewdata, gdy tylko je znajdzie, odbuduje selectList i zignoruje oryginalny Selected.
Problem w tym, że w wielu przypadkach tak naprawdę nie używamy tego w ten sposób. chcemy po prostu wrzucić selectList z jednym / wieloma elementami Selected set true.
Oczywiście rozwiązanie jest proste, użyj nazwy, której nie ma w modelu ani w viewdata. Jeśli nie może znaleźć dopasowania, użyje oryginalnej listy selectList, a oryginalny Selected będzie mieć wpływ.
Ale nadal uważam, że mvc powinno to poprawić, dodając jeszcze jeden warunek
if ((defaultValue != null) && (!selectList.Any(i=>i.Selected)))
{
selectList = GetSelectListWithDefaultValue(selectList, defaultValue, allowMultiple);
}
Ponieważ jeśli oryginalna lista selectList miała już jedną Selected, dlaczego miałbyś to zignorować?
Tylko moje myśli.