This page looks best with JavaScript enabled

ASP.NET MVC: Edición de colecciones usando Ajax

 ·  ☕ 4 min  ·  ✍️ eiximenis

Nota: Este post ha sido importado de mi blog de geeks.ms. Es posible que algo no se vea del todo "correctamente". En cualquier caso puedes acceder a la versión original aquí

Buenas! El otro día me enviaron la siguiente duda:

Imaginate esto:

class Direccion{

string Calle {get;set;}

int Piso {get;set;}

}

class Cliente {

string Nombre {get;set;}

List Direcciones {get;set;}

}

Imagina que tiene mas propiedades cada clase pero para el ejemplo sirve.

Entonces tengo una Vista para definir la información de Cliente:

Donde van a aparecer los campos para rellenar el cliente. Dentro voy a tener un boton que va a ir agregandome vistas  parciales con la info de Direcciones.

Actualmente la parte de ediccion de Direcciones estaría en una Vista Parcial (con su correspondiente Form).

El problema que tengo es que necesito Enviar todo en el submit de Cliente y realmente es como si no tuviese la información de las direcciones.

Vale, de lo que se trata es que des de la vista para crear un Cliente, podamos añadir objetos Direccion, tantos como sea necesario, usando una vista parcial que iremos cargando via Ajax. El problema era que no se recibía la información de las direcciones.

Hace algún tiempo hablé de las peculiaridades del binding de colecciones en ASP.NET MVC (parte 1, parte 2 y parte 3). Si os miráis la primera parte de la serie, allí comentábamos que nombre debían tener los campos que se enlazaban a una colección. Básicamente si queremos enlazar una colección que está en la propiedad Direcciones de nuestro viewmodel, los campos deben llamarse Direcciones[0], Direcciones1 y así sucesivamente.

Sabiendo esto, queda claro lo que hay que hacer:

  1. Una vista para crear un Cliente. Dicha vista contendrá un
    en el cual iremos cargando las vistas parciales para crear direcciones
  2. Una vista parcial para crear un objeto direccion

Tan solo debemos tener cuidado de incrustar las vistas parciales dentro del

de la vista principal y de seguir la convención de nombres del ModelBinder. Asi pues, empecemos por la vista para crear un Cliente.

Partimos del código que nos genera VS al crear una nueva vista para crear objetos de tipo Cliente:

@model MvcAjaxCol.Models.Cliente

@{
    ViewBag.Title = "Nuevo cliente";
}
<h2>Nuevo Cliente</h2>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script
@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>Cliente</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.Nombre)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Nombre)
            @Html.ValidationMessageFor(model => model.Nombre)
        </div>
        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}

El propio VS no nos genera código para la propiedad “Direcciones” de la clase Cliente porque es una colección. Bueno, el siguiente paso es crear un

vacío donde colocaremos via Ajax las vistas parciales para crear objetos del tipo dirección. Por supuesto dicho
debe estar dentro del . Luego añadimos un botón y cargamos una vista parcial nueva cada vez que se pulse el botón:

@model MvcAjaxCol.Models.Cliente

@{
    ViewBag.Title = "Nuevo cliente";
}

<script type="text/javascript">
    var idx = 0;
    $(document).ready(function() {
        $("#cmdNewDir").click(function() {
            var uri = "@Html.Raw(Url.Action("NewDir", "Home", new {prefix="Direcciones", idx="xxx" }))";
            uri = uri.replace("xxx", idx);
            idx++;
            $.get(uri, function(data) {
                $("#direcciones").append(data);
            });
        });
    });

</script>
<h2>Nuevo Cliente</h2>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
@using (Html.BeginForm())
{
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>Cliente</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.Nombre)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Nombre)
            @Html.ValidationMessageFor(model => model.Nombre)
        </div>
        <hr />
        <input type="button" value="Nueva Direccion" id="cmdNewDir"/>
        <div id="direcciones"></div>
        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}

Como se puede a cada click del botón llamamos a una acción del controlador llamada NewDir. Esta acción es la que devuelve la vista parcial que añadimos al div:

public ActionResult NewDir(string prefix, string idx)
{
   ViewData["prefix"] = prefix;
   ViewData["idx"] = idx;
   return PartialView("Direccion");
}

Y finalmente la vista Direccion, usa los parámetros prefix y idx para ir generando campos cuyo nombre sea prefix[idx].XXX:

@model MvcAjaxCol.Models.Direccion
<div class="editor-label">
    @Html.LabelFor(model => model.Calle)
</div>
<div class="editor-field">
    @Html.Editor(string.Format("{0}[{1}].{2}", ViewData["prefix"], ViewData["idx"], "Calle"))
    @Html.ValidationMessage(string.Format("{0}[{1}].{2}", ViewData["prefix"], ViewData["idx"], "Calle"))

</div>

<div class="editor-label">
    @Html.LabelFor(model => model.Piso)
</div>
<div class="editor-field">
    @Html.Editor(string.Format("{0}[{1}].{2}", ViewData["prefix"], ViewData["idx"], "Piso"))
    @Html.ValidationMessage(string.Format("{0}[{1}].{2}", ViewData["prefix"], ViewData["idx"], "Piso"))
</div>

¡Y listos! Con esto ya podemos ir añadiendo tantas direcciones como sea necesario a nuesro cliente!

¡Un saludo!

Si quieres, puedes invitarme a un café xD

eiximenis
ESCRITO POR
eiximenis
Compulsive Developer