Pensando un poco se me ocurrió un ejemplo donde nos podría ser útil esta nueva propiedad, así que supongamos que tenemos una colección de elementos de este tipo
public class Item { public int IdItem { get; set; } public string Description { get; set; } public int Quantity { get; set; } }Esta colección de elementos se la tenemos que presentar al usuario y este debe escribir las cantidades que quiere para cada Item, y enviarla de nuevo al servidor. Se podría ver como un versión muy reducida de un carrito de la compra, donde tenemos los elementos que queremos comprar y podemos modificar la cantidad de elementos. Bien, para mostrar esta información y para simplificar el ejemplo he creado la siguiente modelo y acciones
Modelo
public class Model { public List<Item> Items { get; set; } }Acciones
public ActionResult Index() { Model model = new Model(); FillModel(model); return View(model); } [HttpPost()] public ActionResult Index(Model model) { return RedirectToAction("Index"); } private void FillModel(Model model) { model.Items = new List<Item>(); for (int i = 1; i <= 10; i++) model.Items.Add(new Item() { IdItem = i, Description = string.Format("Item{0}", i), Quantity = 0 }); }Vista
<h2>Items</h2> @using (Html.BeginForm()) { for(int i = 0; i < Model.Items.Count; i++) { <div> @Html.HiddenFor(m => Model.Items[i].IdItem) @Html.HiddenFor(m => Model.Items[i].Description) @Model.Items[i].Description @Html.TextBoxFor(m => Model.Items[i].Quantity) </div> } <input type="submit" value="Enviar" /> }El aspecto de este formulario sería el siguiente
Vista |
Bien, si modificamos alguna de las cantidades y le damos a enviar, obtendremos esto en la controladora
Post |
Como vemos el valor introducido por el usuario (10) ha sido "bindeado" en nuestro modelo sin problemas. Bien, ahora supongamos que queremos hacer lo mismo pero usando knockout. Para eso, debemos modificar ligeramente la vista de la siguiente manera.
@using (Html.BeginForm()) { <!-- ko foreach: items --> <div> <input type="hidden" data-bind="value: IdItem" /> <input type="hidden" data-bind="value: Description" /> <span data-bind="text: Description"></span> <input type="text" data-bind="value: Quantity" /> </div> <!-- /ko --> <input type="submit" value="Enviar" /> } <script type="text/javascript"> $(document).ready(function () { function ViewModel(model) { var self = this; self.items = ko.observableArray(model.Items); } var viewModel = new ViewModel(@Html.Raw(Json.Encode(Model))); ko.applyBindings(viewModel); }); </script>Con esto veremos que nuestra vista es igual a la obtenida anteriormente, pero al enviar el form nos daremos cuenta que nuestro modelo no se "bindea" correctamente.
Si sabemos como funciona el bind de ASP.NET MVC veremos que este error es lógico ya que los elementos que estamos poniendo en nuestra vista no tienen el atributo name definido, pero además hay que tener en cuenta que estamos intentando "bindear" una colección por lo que tendremos que especificar el índice que ocupa el elemento en la colección. Para esto es para lo que viene de maravilla el uso de la propiedad $index dentro del contexto foreach (no es que antes no se pudiera hacer, sino que era más complicado). Con esto la vista quedaría de la siguiente manera
<!-- ko foreach: items --> <div> <input type="hidden" data-bind="value: IdItem, attr: {name: 'Items['+$index()+'].IdItem'}" /> <input type="hidden" data-bind="value: Description, attr: {name: 'Items['+$index()+'].Description'}" /> <span data-bind="text: Description"></span> <input type="text" data-bind="value: Quantity, attr: {name: 'Items['+$index()+'].Quantity'}" /> </div> <!-- /ko -->Esto generaría el siguiente código html que vemos que cumple con lo dicho anteriormente
Vista HTML |
Y si hacemos el post veremos que el modelo se ha "bindeado" de nuevo correctamente
NOTA
Hay otra forma de generar la vista de una manera más "elegante" y evitando así tener que usar un bucle y teniendo a nuestra alcance el Intellisense es la siguiente. Creamos una página parcial (Partial Page) llamada Item.cshtml (mismo nombre que la clase que contiene la colección) en el directorio View/Home/RenderPartials o Shared/RenderPartials y en ella pondremos lo siguiente
@model test.web.mvc.cs.Models.Item <div> @Html.HiddenFor(m => m.IdItem) @Html.HiddenFor(m => m.Description) @Model.Description @Html.TextBoxFor(m => m.Quantity) </div>Luego en la vista tan solo deberemos poner lo siguiente
@using (Html.BeginForm()) { @Html.EditorFor(x => x.Items) <input type="submit" value="Enviar" /> }
Happy coding!
No hay comentarios:
Publicar un comentario