ASP.NET MVC MultiSelectList с выбранными значениями, которые не выбраны должным образом
Я знаю, что другие задали этот вопрос, но я полностью смущен этим:
Отображается выпадающий список без выбранных значений:
<%= Html.DropDownList("items", new MultiSelectList(Model.AvailableItems,
"id", "name", Model.items), new { multiple = "multiple" })%>
Отображает выпадающий список со значениями, которые я передаю (Model.items), правильно подобранными, как ожидалось:
<%= Html.DropDownList("somethingelse", new MultiSelectList(Model.AvailableItems,
"id", "name", Model.items), new { multiple = "multiple" })%>
Но проблема в том, что этот элемент теперь называется "somethingelse", когда я POST. Я знаю, что могу взломать это, но что происходит?
Ответы
Ответ 1
У вас есть проблема с Model.Items. Код
<%= Html.DropDownList("items", new MultiSelectList(Model.AvailableItems,
"id", "name", Model.items), new { multiple = "multiple" })%>
фактически не работает так, как вы ожидали. Он работает, потому что имя выпадающего списка - "элементы". Это потому, что в вашем действии появился параметр param, называемый "items". Этот параметр хранится в действии ViewState (не путайте с ViewData).
Html.DropdownList() видит, что существует параметр ViewState с именем так же, как вы назвали свой раскрывающийся список, и использует этот параметр ViewState для выработки выбранных значений. Он полностью игнорирует файлы Model.items, которые вы передали.
Если кто-нибудь может объяснить логику неспособности переопределить поведение по умолчанию, я бы хотел его услышать.
Итак, это ваша первая проблема. Чтобы обойти это все, что вам нужно сделать, это переименовать раскрывающееся меню на что-то другое - точно так же, как вы делали в своем втором примере. Теперь ваша вторая проблема приходит в игру: список выбранных элементов должен быть набором простых объектов (я думаю, что на самом деле это должен быть IEnumerable, но я не уверен на 100%).
Метод DropDownList() попытается сопоставить эти выбранные значения со значением в вашей коллекции AvailableItems
. Если он не может этого сделать, он попытается сопоставить текст.
Итак, попробуйте это, чтобы увидеть, работает ли он
<%= Html.DropDownList("somethingelse", new MultiSelectList(Model.AvailableItems,
"id", "name", Model.items.Select(c=> c.name)), new { multiple = "multiple" })%>
Удачи.
Ответ 2
Слишком маленький контекст в вашем вопросе, но я попытаюсь показать полный рабочий пример:
Модель:
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
}
public class MyModel
{
public IEnumerable<int> SelectedItemIds { get; set; }
public IEnumerable<Item> AvailableItems {
get
{
return new[]
{
new Item { Id = 1, Name = "Item 1" },
new Item { Id = 2, Name = "Item 2" },
new Item { Id = 3, Name = "Item 3" },
};
}
}
}
Контроллер:
[HandleError]
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new MyModel
{
SelectedItemIds = new[] { 2, 3 }
};
return View(model);
}
[HttpPost]
public ActionResult Index(IEnumerable<int> selectedItemIds)
{
var model = new MyModel
{
// Important: Don't ever try to modify the selectedItemIds here
// The Html helper will completely ignore it and use
// the POSTed values
SelectedItemIds = selectedItemIds
};
return View(model);
}
}
Вид:
<% using (Html.BeginForm()) { %>
<%= Html.ListBoxFor(x => x.SelectedItemIds,
new MultiSelectList(Model.AvailableItems, "Id", "Name")) %>
<input type="submit" value="GO" />
<% } %>
Обратите внимание, что Html.ListBoxFor
более адаптирован, если вы хотите сгенерировать множественный выбор. Очевидно, свойство AvailableItems
должно быть извлечено из репозитория.
Ответ 3
У меня была та же проблема, я использовал свой собственный метод расширения для генерации html и проблемы, разрешенной
public static MvcHtmlString ListBoxMultiSelectFor<TModel, TProperty>(
this HtmlHelper<TModel> helper,
Expression<Func<TModel, TProperty>> expression,
IEnumerable<SelectListItem> selectList,
object htmlAttributes)
{
return ListBoxMultiSelectFor(helper, expression, selectList, new RouteValueDictionary(htmlAttributes));
}
public static MvcHtmlString ListBoxMultiSelectFor<TModel, TProperty>(
this HtmlHelper<TModel> helper,
Expression<Func<TModel, TProperty>> expression,
IEnumerable<SelectListItem> selectList,
IDictionary<string, object> htmlAttributes)
{
string name = ExpressionHelper.GetExpressionText(expression);
TagBuilder selectTag = new TagBuilder("select");
selectTag.MergeAttributes(htmlAttributes);
selectTag.MergeAttribute("id", name, true);
selectTag.MergeAttribute("name", name, true);
foreach (SelectListItem item in selectList)
{
TagBuilder optionTag = new TagBuilder("option");
optionTag.MergeAttribute("value", item.Value);
if (item.Selected) optionTag.MergeAttribute("selected", "selected");
optionTag.InnerHtml = item.Text;
selectTag.InnerHtml += optionTag.ToString();
}
return new MvcHtmlString(selectTag.ToString());
}
Ответ 4
На самом деле, если вы посмотрите на исходный код MVC, это поведение вызывается в DropDownListFor по умолчанию (поиск allowMultiple: false). Решение состоит в том, чтобы вместо этого использовать ListBoxFor (вы увидите это также в исходном коде MVC, allowMultiple: true), что делает большой смысл как HTML мудрым, оба визуализируются на
<select ...>
<option ...>
<option ...>
...
</select>
Вам не нужно использовать разные свойства модели, как предложено в ответах выше этого, я получил эту работу, просто переключившись на ListBoxFor вместо этого (CSS берет ее оттуда):
@Html.ListBoxFor(model => model.SelectedCategories,
new MultiSelectList(Model.Categories, Model.SelectedCategories),
new { multiple = "multiple" })
Работает как шарм, даже с POST и повторным отображением представления при ошибке.
Ответ 5
Вы можете перейти к значению "элементов" с помощью этого
<HttpPost()> _
Function Edit(ByVal crm_cliente As crm_cliente, ByVal form As FormCollection) As ActionResult
If ModelState.IsValid Then
Dim items As String
crm_cliente.usuario_modifico = "ejmorales"
crm_cliente.fecha_modifico = Date.Now
items = form("items")
который предоставит вам выбранные элементы в виде строки, разделенной запятыми (,)