c# - mvc - razor html dropdownlist multiselect
¿Cómo persisten todos los valores en una selección realizada desde @ Html.ListBoxFor en una página web de MVC.net? (3)
Un ejemplo que usa el resultado de Json y la vista de plantilla. Este es un buen patrón.
Controlador (puede configurar json para no permitir obtener):
public class FornecedorController : BaseController
{
protected FornecedorServices Service = new FornecedorServices();
[HttpGet]
[Authorize(Roles = ApplicationRoles.FORNECEDOR_VISUALIZAR)]
[OutputCache(NoStore = false, Duration = 3600)]
public JsonResult ListarJson(FornecedorParameters parameters)
{
var model = this.Service.Search(parameters)
.Select(x => new
{
Value = x.Codigo,
Description = x.CodigoNomeFantasia
});
return this.Json(model, JsonRequestBehavior.AllowGet);
}
}
Vista de plantilla (puede personalizar la suya):
@model int[]
@{
var id = "id" + Guid.NewGuid().ToString().Substring(0, 5);
var disabled = (bool)(this.ViewData["disabled"] ?? false);
var showAll = (bool)(this.ViewData["ShowAll"] ?? false);
var state = this.ViewData.ModelState[Html.NameFor(x => x).ToString()];
var size = (Size)(this.ViewData["Size"] ?? Size.Big);
string css = (state != null && state.Errors.Count > 0) ? "input-validation-error" : string.Empty;
List<SelectListItem> listValues;
if (this.Model == null)
{
listValues = new List<SelectListItem>();
}
else
{
listValues = this.Model.Select(x => new SelectListItem { Selected = true, Value = x.ToString(), Text = x.ToString() }).ToList();
}
}
<div class="field-@size @css">
<h3>@Html.LabelFor(model => model):</h3>
@Html.ListBox("", listValues, new { id = id })
</div>
<script language="javascript" type="text/javascript">
$("#@id").turnAutoComplete("@Url.Action("ListarJson", "Fornecedor", new { ShowAll = showAll })"@if (showAll) { <text>, checkSelectAll</text> })
.change(function () {
@Html.Raw(this.ViewData["OnChange"])
});
</script>
El problema: genero un @Html.ListBoxFor
basado en una consulta de base de datos en mi HttpGet. Durante mi HttpPost, quiero validar que al menos un elemento ha sido seleccionado. Si no, solo quiero agregar un mensaje de validación.
El resultado actual: recibo el mensaje "Por favor, seleccione al menos un elemento", pero ahora la selección está en blanco (el elemento seleccionado está allí pero contiene 0 opciones). Entiendo que Model.Items
será nulo en mi HttpPost.
Pregunta: ¿Cómo puedo usar mi modelo para persistir Model.Items
para que no sea nulo?
Información adicional: estoy tratando de evitar el uso de la FormCollection collection
y JavaScript adicional.
--El código--
Controlador:
public class HomeController : Controller
{
public ActionResult Index()
{
MyViewModel model = new MyViewModel
{
Items = Enumerable.Range(1, 5).Select(x => new SelectListItem
{
Value = x.ToString(),
Text = "item " + x
})
};
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
}
Modelo:
public class MyViewModel
{
public MyViewModel()
{
Items = new List<SelectListItem>();
}
[Required(ErrorMessage = "Please select at least one item")]
public string[] SelectedItems { get; set; }
public IEnumerable<SelectListItem> Items { get; set; }
}
Ver:
@model ProjectGenerator.Models.MyViewModel
@using (Html.BeginForm())
{
@Html.ListBoxFor(x => x.SelectedItems, Model.Items)
@Html.ValidationMessageFor(x => x.SelectedItems)
<button type="submit">OK</button>
}
Usted no es, ni debería, crear controles de formulario para cada propiedad de cada SelectListItem
de SelectListItem
de SelectListItem
en Items
propiedad para que no se incluyan en los datos del formulario cuando envíe. SelectList
volver a asignar SelectList
en el método POST de devolver la vista
public ActionResult Index()
{
MyViewModel model = new MyViewModel();
ConfigureViewModel(model);
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
if (!ModelState.IsValid)
{
ConfigureViewModel(model);
return View(model);
}
// Save and redirect
}
private void ConfigureViewModel(MyViewModel model)
{
model.Items = Enumerable.Range(1, 5).Select(x => new SelectListItem
{
Value = x.ToString(),
Text = "item " + x
});
}
public IEnumerable<SelectListItem> Items
{
get
{
if (HttpContext.Current.Session["MY_ITEMS_TO_LIST_FOR"] == null)
{
return null;
}
else
{
return (IEnumerable<SelectListItem>)HttpContext.Current.Session["MY_ITEMS_TO_LIST_FOR"];
}
}
set
{
if (value.Count() > 0) //http post reset value
{
HttpContext.Current.Session["MY_ITEMS_TO_LIST_FOR"] = value;
}
}
}
Probé de esta manera y funcionó bien. Hay otras formas, pero encontré esto más fácil. Si depura el conjunto de elementos de la propiedad Items, verá que, por algún motivo, los elementos se eliminan de la colección en http post, incluso si usa session. Para evitar que la colección en la sesión reciba una colección vacía, utilicé If (Items.Count ()> 0). Puede ampliar esta idea y personalizar su get y set.