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

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<Direccion> 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], Direcciones[1] y así sucesivamente.

Sabiendo esto, queda claro lo que hay que hacer:

  1. Una vista para crear un Cliente. Dicha vista contendrá un <div> 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 <form> 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 <div> vacío donde colocaremos via Ajax las vistas parciales para crear objetos del tipo dirección. Por supuesto dicho <div> debe estar dentro del <form>. 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!

5 comentarios sobre “ASP.NET MVC: Edición de colecciones usando Ajax”

  1. Hola Eduard,

    Excelente artículo.

    Una consulta. Cada ves que creas una nueva dirección vas a servidor para que te devuelva un PartialView, ¿hay forma de no ir a servidor y cargar el DIV de nueva dirección usando javascript?

    Saludos,
    Arturo

  2. ar7uro, no soy Edu, pero creo que lo que planteas podría hacerse definiendo la plantilla en js : por ejemplo en el onReady podrías traerlo desde el server, o definirlo a pelo….

    Y en el momento de hacer el agregar una nueva dirección, hacer los cambios pertinentes para que este nombrado de forma correcta y añadirlo al div de direcciones.

  3. Buenas!
    @ar7uro, como dice Javier si que se puede hacer.

    A ver si saco tiempo y monto un ejemplo!!! 😀

    Saludos y gracias por comentar! 😉

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *