Trabajar con un modelo MVC mediante WCF + jQuery

Después de mucho tiempo sin dejarme ver, ni leer por aquí, me he animado a escribir este artículo, como va siendo habitual últimamente, debido a un comentario escrito en otro de mis posts.

Sabemos que existen distintas maneras de interactuar con las entidades de nuestro modelo de datos, pero muchas veces nos cuesta “innovar” si no tenemos un punto de partida. En este caso vamos a ver, paso a paso, como podemos trabajar de manera sencilla con nuestro modelo en una aplicación MVC mediante la combinación de un servicio WCF + jQuery.

Lo primero que vamos a hacer es crear la aplicación web correspondiente. Para ello abrimos Visual Studio y seleccionamos la opción File > New > ASP. NET MVC3 Web Application, y creamos nuestra aplicación a la que llamaremos JqueryModel.

imageimage

Seleccionamos la plantilla Internet Application, y el View Engine ASPX. * También podemos indicar que se cree un proyecto de pruebas unitarias asociado.

Una vez creada la aplicación:

1. Creamos la base de datos que vamos a ir alimentando a través de nuestro sitio web, mediante un formulario de contacto. Dentro de App_Data hacemos clic con el botón derecho y seleccionamos la opción Add>New Item >SQL Server Database y creamos una base de datos llamada ContactosDB

image

Donde añadiremos una tabla llamada Contacto, donde el identificador (Id) será la clave primaria, y la cual contendrá los siguientes campos:

image

2. Creamos el modelo de datos de tipo de ADO.NET Entity Data Model,  a partir de nuestra base de datos y que se llamará ModeloContactos

imageimage

3. Añadimos servicio WCF. Para ello seleccionamos un nuevo Item de tipo AJAX-enabled WCF Service, y al que llamaremos Ajaxservice

image

En dicho archivo debemos añadir el siguiente código:

1. Referencia al modelo:

  1. using JQueryModel.Models;

2. La definición de nuestra entidad de modelo:

private JQueryModel.Models.ContactosDBEntities1 contactoentity= new JQueryModel.Models.ContactosDBEntities1();

3. El método de creación de objetos de tipo contacto, que llamaremos insertarContacto:

public object insertarContacto(string Nombre, string Apellido, string Movil, string Email)
        {

            try
            {
                Contacto contacto = new Contacto
                {
                    Nombre = Nombre,
                    Apellido = Apellido,
                    Movil = Movil,
                    Email = Email
                };

                contactoentity.AddToContacto(contacto);
                contactoentity.SaveChanges();

                return true;
            }
            catch (Exception ex)
            {
                
                return false;
            }

        }

A continuación vamos a personalizar la vista que queremos mostrar. Para ello abrimos el archivo Index.aspx, que es el que se llama por defecto desde el controlador principal HomeController.cs, al cargarse la página. En él:

1. Añadimos las referencias a los archivos javascript que vamos a utilizar, tras añadirlos previamente en caso de que no estén en la aplicación:

<script src=»../../Scripts/jquery-1.5.1.js» type=»text/javascript»></script>
<script src=»../../Scripts/json2.js» type=»text/javascript»></script>

El segundo de ellos se utiliza para serializar JSON, ya que para llamar a un servicio WCF desde jQuery la petición debe estar bajo la notación JSON, por lo que necesitamos una biblioteca externa que nos de dicha funcionalidad. *Descargada de github.com/…/JSON-js

2. Dentro del contenido principal añadimos los campos del formulario con su correspondiente identificador que serán de tipo Input, para que luego podamos llamarlos desde el código javascript. Para ello utilizamos el siguiente código HTML:

    <h2>Contacto</h2>
<table>
    <tr>
        <th>Nombre:</th>
        <td stylewidth: 246px»><input id=»Nombre» stylewidth: 244px» /></td>
    </tr>
    <tr>
        <th>Apellido:</th>
        <td stylewidth: 246px»><input id=»Apellido» stylewidth: 244px» /></td>
    </tr>
    <tr>
        <th>Movil:</th>
        <td stylewidth: 246px»><input id=»Movil» stylewidth: 244px» /></td>
    </tr>
    <tr>
        <th>Email:</th>
        <td stylewidth: 246px»><input id=»Email» stylewidth: 244px» /></td>
    </tr>
    <tr>
        <th><button id=»BotonAgregar»>Añadir contacto</button></th>
    </tr>
</table>

A continuación añadimos el código javascript correspondiente que llamará al método definido dentro de nuestro servicio, al hacer clic en el botón:

<script type=»text/javascript»>
        $(«#BotonAgregar»).click(function () {
            //Convertir los datos del formulario en un objeto
            var objeto = {
                Nombre: $(«#Nombre»).val(),
                Apellido: $(«#Apellido»).val(),
                Movil: $(«#Movil»).val(),
                Email: $(«#Email»).val() };

            //Parseamos nuestro objeto a JSON
            data = JSON.stringify(objeto);

            $.ajax({
                type: «POST»,
                contentType: «application/json; charset=utf-8»,
                url: «AjaxService.svc/insertarContacto»,
                data: data,
                dataType: «json»,
                success: insertarCallback
            });
        });

        //Mensajes de ejecución
        function insertarCallback(result) {
            result = result[«d»];
            if (result === true) {
                alert(«Contacto Agregado»);
            } else {
                alert(«Error al tratar de Grabar el contacto»);
            }
        }
</script>

Si compilamos la aplicación y probamos a añadir contactos, vemos que salta la alerta configurada en caso de que el método funcione correctamente…lo que es buena señal.

imageimage

Y si accedemos a nuestra base de datos, vemos como los nuevos contactos se han agregado correctamente:

image

Ruegos y preguntas en http://geeks.ms/blogs/gortigosa/contact.aspx para todo lo demás…

[Libro] Tácticas de guerrilla para desarrolladores

Ahora que nos encontramos en la “era” de las metodologías agiles, me parece interesante recuperar un artículo que escribí hace algún tiempo en otro blog, en referencia a un libro que habla de los principales problemas que encontramos los desarrolladores a nivel de gestión de un proyecto y algunas de las soluciones que podemos tomar “desde dentro” para solventar o, al menos minimizar dichos problemas:

En este artículo para variar, no voy a mostrar ningún tipo de plataforma novedosa, ni ninguna aplicación práctica ni, por supuesto, nada de código. Voy a hablar de un libro que estoy leyendo y cuya lectura debo agradecer a un compañero… ¡¡gracias Carlos!!

Este libro no es otro que «The Career: guerrilla tactics for an imperfect world» y que nada tiene que ver con tácticas de guerrilla para conquistar el mundo…bueno sí, el mundo de la programación. A través de él, el autor, nos plantea la situación típica que se suele dar en los proyectos de desarrollo y las «tácticas» que podemos emplear para solventar los problemas que van surgiendo a lo largo del mismo.

Capítulo a capítulo se exponen, entre otras muchas cosas, las dificultades que encuentran los desarrolladores y «las personas de negocios» para comunicarse, que problemas o imprevistos nos surgen durante el desarrollo de un proyecto, y por último, que es lo que podemos hacer o como podemos evitar esto para garantizar que ambas partes consigan lo que quieren. Mientras los empresarios, obtienen beneficios en el menor periodo de tiempo, los desarrolladores sufren menos estrés, obtienen una mayor calidad del producto y consiguen una carrera más satisfactoria. Realmente compensa ¿no?

De todos los temas que se tratan en el libro voy a tratar de sintetizar la información más interesante y me voy a centrar en lo expuesto en las dos primeras partes. En la primera de ellas, Christopher Duncan cuenta como en el mundo de los negocios, muchas veces se plantea una guerra entre los desarrolladores y las personas de negocios. Nos viene a decir que cuando estamos creando un producto de tipo software, los programadores debemos ser capaces de hablar en un «lenguaje» en el que los empresarios nos entiendan. De nada sirven nuestros conocimientos técnicos si no sabemos «vender» lo que estamos haciendo a aquellas personas que, queramos o no, son las que se encargan de fijar los plazos de desarrollo, decidir el alcance del proyecto… y despedir a quienes no cumplan sus demandas. Para ello, lo primero que tenemos que hacer es ser conscientes de que en el mundo de la empresa existen múltiples factores que pueden influir en el desarrollo de un proyecto software: las políticas internas de la empresa, calendarios poco realistas, una gestión incorrecta, la falta de recursos humanos y materiales… Lo que lleva consigo un desgaste por parte del desarrollador que, generalmente, se traduce en horas extras continuas y niveles altos de estrés.

Para evitar todo esto, lo que el autor sugiere es aprender a comunicarnos con las personas de negocios de nuestra empresa y ser capaces de transmitir en su «lenguaje» que es lo que estamos haciendo, cómo y cuanto tiempo nos va a llevar. De esta manera podemos tener una oportunidad para gestionar nuestros proyectos de forma exitosa y conseguir, por ejemplo, que se establezcan fechas de entrega más reales, que se empleen los recursos adecuados y que no se nos pidan cosas que no se pueden hacer (¡o por lo menos no de la forma en la que nos las piden!).

En la segunda parte se habla de las pautas que debemos seguir para evitar los problemas que suelen aparecer durante el desarrollo de una aplicación software que, en general, están relacionados con una gestión incorrecta del proyecto o con unos tiempos de entrega irreales. Este último es uno de los factores que más estrés genera al desarrollador, ya que en la mayoría de los casos la lista de características de un proyecto va cambiando o aumentando sobre la marcha, mientras que los tiempos de entrega se mantienen. Así que por regla de tres: + Características en Tiempo X = >+ Nº Horas

Para evitar esto, lo que el autor propone, es definir y entender el proceso de nuestra aplicación correctamente, estimar los esfuerzos que vamos a dedicar al diseño y a la implementación de la misma, y por último gestionar nuestra gestión. Es decir, nadie mejor que nosotros mismos, sabe cuáles son nuestras capacidades y nuestras limitaciones, por lo tanto dentro de la gestión global del proyecto debemos ser capaces de gestionar nuestro propio tiempo. De forma que si conseguimos optimizar el tiempo de desarrollo en función de nuestras aptitudes reduciremos, en parte, el estrés generado o las horas extras continuadas, pero esto no depende únicamente de nosotros…

En definitiva, este libro está diseñado para ayudarnos a ser mejores programadores dentro del mundo de los negocios, que al fin y al cabo es en el mundo en el que nos movemos. Así que si alguien quiere ver lo tratado en este artículo en profundidad, y otros aspectos relacionados, ya sabéis…¡A leer!”

¿Y qué es lo que quiero transmitir recuperando este artículo? Que todos los que ya estamos subidos en el carro del agilismo, hemos visto un poco la luz al final de este túnel, hasta ahora, muy habitual dentro del mundo del software. Somos conscientes de cuanto ha ayudado a mejorar la gestión de nuestros proyectos el uso de las metodologías agiles, a todos los niveles: gestión del tiempo, los recursos, los costes y especialmente, las personas, y desde aquí os animo a que lo probéis en vuestras empresas.

Check-out múltiple automático en SharePoint 2007

Uno de los múltiples usos que ofrece la plataforma SharePoint es el de gestor documental. Si en nuestra empresa vamos a trabajar con SharePoint con esta finalidad, es necesario tener claro cuales son las características, y funcionalidades que queremos que presenten nuestras bibliotecas. Uno de los aspectos a tener en cuenta, es la posibilidad de realizar versionado de documentos, y la protección/desprotección de los mismos. Si esto último no es algo que nos preocupe excesivamente, lo primero que debemos hacer es desactivar dicha opción, de esta forma evitaremos trabajo “manual” posterior.

image

Sin embargo, si queremos que la característica de protección/desprotección este activada, ya que aporta integridad a nuestros documentos, debemos tener cuidado con una acción que realizamos de forma cotidiana. Cuando trabajamos con gran cantidad de documentos, tendemos a usar herramientas que simplifican el cargado de los mismos en nuestra plataforma(intranet, extranet, sitio web…) como puede ser la “subida masiva” mediante la opción Abrir en Windows Explorer.

image 

Cuando realizamos esta operación, los documentos adjuntados se encuentran desprotegidos, y por lo tanto inaccesibles al resto de los usuarios. De forma que para que sean visibles, sería necesario ir protegiéndolos uno a uno, por lo tanto el tiempo que hemos ahorrado con la subida masiva…ya no es tanto. Entonces  ¿cómo podemos hacer este proceso de forma automática?  Una de las opciones sería utilizar un flujo de trabajo, que se lance cada vez que añadamos un elemento a nuestra biblioteca y que se encargue de realizar el check in de manera automática. Esto se podría implementar mediante un flujo diseñado con SharePoint Designer:

image

O a través de un elemento web personalizado, usando la librería propia de SharePoint(Microsoft.SharePoint), donde podemos encontrar la clase SPFile, que nos permite trabajar con archivos. Esta, cuenta con el método CheckIn, que nos permite realizar la protección de archivos programáticamente. El código a implementar sería el siguiente:

image 

*Este código se puede adaptar indicando la colección de sitios, los sitios, o las bibliotecas específicas, en función de cada caso concreto.

Aunque es algo sencillito, espero que os sirva de ayuda, ya que es una duda que se repite bastante:)

[ASP.NET MVC]…”Más Cositas” que podemos hacer con jQuery

Siguiendo en la línea del último artículo, vamos a ver otra aplicación sencilla que podemos implementar utilizando jQuery. En este caso vamos a programar un sistema de valoración mediante el cual los usuarios pueden votar a los distintos equipos que se muestran publicados en nuestra página web. Para ello partimos de la aplicación Example1, que utilizamos en un articulo anterior, y vamos a ir realizando las siguientes modificaciones:

1. Añadimos las imágenes (EmptyStar.png, FilledStar.png) que vamos a utilizar para conseguir el efecto deseado al mover el ratón sobre las mismas (EmptyStar FilledStar )

image

2. Dentro del controlador TeamController, donde mostramos los equipos almacenados en nuestro modelo de datos, añadimos un resultado de acción que se encargará de cargar el valor que seleccionamos a través de la vista en formato json. Para ello ejecutamos el siguiente código:

  1. [AcceptVerbs(HttpVerbs.Post)]
  2. public JsonResult Rate(string rating)
  3. {
  4.     return Json("Valorado como " + rating);
  5.     
  6. }

2. Dentro de la vista Index:

– Creamos una nueva columna llamada Valoración,

  1. <th>
  2.     Valoración
  3. </th>

– Donde añadiremos los elementos de la valoración (“estrellas”), que definimos de la siguiente manera:

  1. <td>
  2.     <p>
  3.         <img src="../../Content/Imagenes/EmptyStar.png" alt="Star Rating" align="middle" id="1" title="Muy malo"/>
  4.         <img src="../../Content/Imagenes/EmptyStar.png" alt="Star Rating" align="middle" id="2" title="Malo"/>
  5.         <img src="../../Content/Imagenes/EmptyStar.png" alt="Star Rating" align="middle" id="3" title="Regular"/>
  6.         <img src="../../Content/Imagenes/EmptyStar.png" alt="Star Rating" align="middle" id="4" title="Bueno"/>
  7.         <img src="../../Content/Imagenes/EmptyStar.png" alt="Star Rating" align="middle" id="5" title="Excelente"/>
  8.     </p>
  9. </td>

Inicialmente el formato de la columna valoración es este image . Cada una de las imágenes se identifica mediante un id y un title específico. Este último atributo es el que se devuelve como parámetro a la acción Rate cuando hacemos clic sobre el sistema de valoración, y que nos permite mostrar el valor seleccionado.

– Finalmente, añadimos la referencia al código jquery para que la vista funcione correctamente.

  1. <script language="javascript" type="text/javascript" src="../../Scripts/jquery-1.4.1.js"></script>

– Y el código jQuery correspondiente que nos permite realizar la valoración. Dentro de dicho código vamos a implementar 3 eventos.

1. Mouseover (cuando pasamos el ratón por encima de las imágenes). Se ejecuta la función giveRating, mediante la cual se carga la imagen FilledStar.png, y se aplica un estilo específico.

2.Mouseout (cuando quitamos el ratón). Se ejecuta la función giveRating, y en este caso cargamos la imagen EmptyStar.png

3.Click (cuando hacemos click sobre una de las imágenes), desvinculamos todos los eventos asociados a dicha imagen (mediante .unbind)  y enviamos la petición post con los parámetros correspondientes:

– url o la acción del controlador

– datos

– función de respuesta, que se ejecuta cuando la acción del controlador devuelve la información json

  1. <script language="javascript" type="text/javascript">
  2.         $(function () {
  3.             $("img").mouseover(function () {
  4.                 giveRating($(this), "FilledStar.png");
  5.                 $(this).css("cursor", "pointer");
  6.             });
  7.  
  8.             $("img").mouseout(function () {
  9.                 giveRating($(this), "EmptyStar.png");
  10.             });
  11.  
  12.             $("img").click(function () {
  13.                 $("img").unbind("mouseout mouseover click");
  14.                 var url = "/Team/Rate?rating=" + $(this).attr("title");
  15.                 $.post(url, null, function (data) {
  16.                     alert(data);
  17.                 });
  18.             });
  19.         });
  20.  
  21.         function giveRating(img, image) {
  22.             img.attr("src", "/Content/Imagenes/" + image)
  23.                .prevAll("img").attr("src", "/Content/Imagenes/" + image);
  24.         }
  25.     </script>

  

Si ejecutamos la aplicación y accedemos a la vista correspondiente, vemos que aparece la columna de Valoración que hemos añadido y en la cual, si nos desplazamos por encima de las “estrellas” vemos el efecto configurado.

image 

Además cuando seleccionamos un valor concreto, nos aparece una alerta con el mensaje correspondiente indicando la valoración realizada, tal y como lo hemos configurado:

image

Y esto es sólo una pequeña muestra…y mejorable, de lo que se puede hacer para conseguir que nuestros sitios web presenten alguna característica 2.0. Si os animáis a contribuir con vuestras ideas, ya sabéis 🙂

[ASP.NET MVC]…”Cositas” que podemos hacer con jQuery I

Últimamente, por motivos laborales, no puedo dedicar el tiempo que me gustaría al blog. Por eso de aquí en adelante aprovecharé a escribir pequeños artículos relacionados con comentarios o preguntas que vayan surgiendo a través del mismo. Así que os invito a que me hagáis llegar vuestras cuestiones, dudas…y en la medida de lo posible intentaré dar respuesta a todas ellas en forma de artículos.

En el caso que vamos a ver hoy, la cuestión que se me planteó, a raíz del post “Crear controles dinámicos mediante jQuery”, es la siguiente:“me gustaría saber como desde el click de un botón puedo crear controles dinámicamente como texbox y label”

Las opciones son múltiples, sobretodo porque jQuery nos aporta una amplia gama de posibilidades y nos ofrece una gran flexibilidad para personalizar nuestras aplicaciones. La que yo he implementado es la siguiente:

1. Creamos una aplicación web MVC 3, y elegimos como motor de vista ASPX. Los que se manejen con Razor también pueden hacerlo con este motor 😉

image

2.Modificamos el código de nuestra vista por defecto, que en nuestro caso es Index.aspx. Dentro de ella:

– Añadimos la referencia al script de jQuery para que podamos implementar sus métodos correspondientes:

  1. <script src=»../../Scripts/jquery-1.4.4.js» type=»text/javascript»></script>

– Creamos los controles que vamos a utilizar. Por un lado definimos un botón que se encargará de lanzar la función write, que nos muestre un textbox, y un texto dentro de este según una serie de parámetros. Y por otro lado definimos el cuadro de texto a mostrar, al que aplicamos el estilo “display:none” para que no aparezca inicialmente.Para ello añadimos el siguiente código:

  1. <button id=»button»>Write</button>
  2. <input id=»dialog» type=»text» styledisplay:none»/>

– Añadimos el correspondiente código javascript, en el que creamos la función write para que reciba tres parámetros(mensaje que queremos mostrar, estado para mostrar el cuadro de texto, y el estado para mostrar el texto). Si state es true, nos mostrará todos los controles de tipo input que hayamos añadido, por la forma que definimos el selector (“input”). Sin embargo si txt es true el texto sólo se añadirá en aquel control cuyo id sea dialog también por la forma que definimos el selector(“#dialog”).

 

En este caso, partiendo de que state es siempre true si el parámetro txt es true, se mostrará tanto el textbox como el texto de prueba.

  1. <script type=»text/javascript»>
  2. jQuery.write = function (message, state,txt) {
  3.     if (state == true) {
  4.         $(«input»).show();
  5.         if (txt == true) {
  6.             $(«#dialog»).val(message);
  7.         }
  8.  
  9.     }
  10. };
  11.  
  12.  $(document).ready(function () {
  13.      $(«#button»).click(function () {   
  14.             $.write(«Esto es una prueba»,true,true)   
  15.             });     
  16.         });
  17. </script>

image 

Si el parámetro txt es false, al hacer clic sobre el botón sólo se mostrará el textbox.

  1. <script type=»text/javascript»>
  2. jQuery.write = function (message, state,txt) {
  3.     if (state == true) {
  4.         $(«input»).show();
  5.         if (txt == true) {
  6.             $(«#dialog»).val(message);
  7.         }
  8.  
  9.     }
  10. };
  11.  
  12.  $(document).ready(function () {
  13.      $(«#button»).click(function () {   
  14.             $.write(«Esto es una prueba»,true,false)   
  15.             });     
  16.         });
  17. </script>

image 

Aunque en este ejemplo hemos utilizado unos parámetros booleanos fijos, aplicados directamente sobre la vista, también se podría pasar un parámetro variable definido mediante el modelo, que será lo que intentaremos ver en el próximo artículo 🙂

Seguro que esta misma funcionalidad ha sido implementada de otra forma diferente, así que desde aquí os animo a comentarlo;)

[Curso]Desarrollo de aplicaciones móviles

En los Centros de Excelencia Software de Navarra queremos dedicar el primer semestre del año a la Movilidad. En un “futuro próximo”, un gran número de usuarios accederá a todo tipo de información a través de dispositivos móviles, por ello las empresas deben estar preparadas para crear nuevas aplicaciones o adaptar las ya existentes a este nuevo entorno de trabajo. Con ese objetivo, se han organizado una serie de cursos dedicados al desarrollo de aplicaciones móviles en distintas plataformas y cuya información se puede encontrar en los siguientes enlaces:

Desarrollo de aplicaciones para Windows Phone7

Desarrollo de aplicaciones para Android

Desarrollo de aplicaciones para iPhone

Desarrollo de aplicaciones para Blackberry

 

Windows-Phone-7 

Además, como complemento a estos cursos se realizarán una serie de jornadas en las que se podrán ver las posibilidades de negocio que ofrecen los teléfonos móviles conectados a internet, para conocer, relacionarse y hacer negocio con los clientes.

«Llega Internet Móvil ¿es importante para tu negocio?»

 «Cómo dar los primeros pasos en el marketing móvil (sin tropezar innecesariamente)»

 «10 ejemplos de acciones de markeging móvil para empresas»

«Cómo aprovechar la movilidad para proporcionar una mejor experiencia a tu cliente»

«Cómo útilizar el móvil para conocer mejor a tu cliente»

Más información aquí

[Evento] Springboard Series Tour en Pamplona

¿Conoces el Springboard Series Tour? Es EL evento para los profesionales de Sistemas TIC, gerentes y directores de Sistemas de cualquier empresa y  cualquier persona en general interesada en la mejor administración de sus sistemas informáticos.

La gira de este año enseñará todo lo que hace falta conocer para desplegar Windows7 y Office 2010 sin problemas, como gestionar los diversos accesos a nuestros sistemas de empresa con seguridad y confianza y todo lo que necesitas saber para virtualizar aplicaciones.

Además, tenemos todo un elenco de ponentes de primera fila:

– Miguel Hernández

– Paulo Días

– Fernando Guillot

– David Nudelman

– Dani Alonso

Miguel HernandezPaulo DiasFernando GuillotDavid NudelmanDani Alonso

Anímate y comparte con nosotros una jornada que ha sido un éxito en toda Europa y que por primera vez traemos a Pamplona.

Toda la información de la jornada está disponible en este enlace de nuestra página web.

[ASP.NET MVC] Ordenar datos en un Datagrid II

En este artículo vamos a ver como se puede realizar la acción de ordenar los elementos de un Datagrid en una aplicación ASP.NET MVC. Para ello vamos a utilizar la aplicación que creamos en el último articulo, y la cual vamos a ir personalizando con las clases, acciones y vistas correspondientes.

Inicialmente vamos a añadir todos los recursos que necesitamos para su correcta implementación. Que son:

– 2 imágenes, que utilizaremos para señalar el tipo de orden que llevan nuestros elementos ascendente/descendente (arrow_down.png, arrow_up.png), y que añadiremos a la carpeta Content

– una clase llamada Dynamic.cs, que se puede descargar aquí, y en la que se encuentran los métodos que nos permiten realizar operaciones dinámicas mediante linq, y que añadiremos en una carpeta llamada HtmlHelpers.

A continuación pasamos a personalizar la clase StoreGridModel, y añadimos las variables que nos permitan indicar la expresión de orden que vamos a aplicar a nuestro modelo:

  1. // Propiedades de ordenación
  2.         public string SortBy { get; set; }
  3.         public bool SortAscending { get; set; }
  4.         public string SortExpression
  5.         {
  6.             get
  7.             {
  8.                 return this.SortAscending ? this.SortBy + " asc" : this.SortBy + " desc";
  9.             }
  10.         }

Dentro del controlador StoreController añadimos un nuevo resultado de acción que por defecto nos devolverá los elementos ordenados en forma ascendente, en función del nombre del “Store”, y que tendrá el siguiente código,  :

  1. public ActionResult Sortable(string sortBy = "Name", bool ascending = true)
  2. {
  3.     var model = new StoreGridModel
  4.     {
  5.         SortBy = sortBy,
  6.         SortAscending = ascending
  7.     };
  8.  
  9.     model.Stores = this.context.Stores.OrderBy(model.SortExpression);
  10.  
  11.     return View(model);
  12. }

Creamos la vista asociada a la acción Sortable, cuyas propiedades son las siguientes:

image

Y añadimos su código correspondiente:

  1. <table class="grid">
  2.     <tr>
  3.         <th><% Html.RenderPartial("SmartLink", Model, new ViewDataDictionary { { "ColumnName", "Name" }, { "DisplayName", "Store" } }); %></th>
  4.         <th><% Html.RenderPartial("SmartLink", Model, new ViewDataDictionary { { "ColumnName", "Customer.AccountNumber" }, { "DisplayName", "Customer" } }); %></th>
  5.         <th><% Html.RenderPartial("SmartLink", Model, new ViewDataDictionary { { "ColumnName", "SalesPersonID" }, { "DisplayName", "SalesPersonID" } }); %></th>
  6.  
  7.     </tr>
  8.  
  9. <% foreach (var item in Model.Stores)
  10.     { %>
  11.     <tr>
  12.         <td class="left"><%= item.Name%></td>
  13.         <td class="left"><%= item.Customer.AccountNumber%></td>
  14.         <td class="left"><%= item.SalesPersonID%></td>
  15.     </tr>
  16.  
  17. <% } %>
  18.     
  19. </table>

Tal y como se observa en esta vista, estamos indicando que se rendericen distintas vistas parciales de nuestro modelo, basadas en SmartLink, pero en cada una de ellas se mostrará el modelo según los parámetros de entrada seleccionados. Las propiedades de dicha vista son las siguientes:

image

Y el código de la misma, en la que se aplican una serie de atributos html en función del nombre de la columna seleccionada y del orden, será:

  1. <%
  2.     var isDescending = string.CompareOrdinal(Model.SortBy, ViewData["ColumnName"].ToString()) == 0 && Model.SortAscending;
  3.     var routeData = new RouteValueDictionary { { "sortBy", ViewData["ColumnName"].ToString() }, { "ascending", !isDescending } };
  4.     var htmlAttributes = new Dictionary<string, object>();
  5.     if (string.CompareOrdinal(Model.SortBy, ViewData["ColumnName"].ToString()) == 0)
  6.     {
  7.         if (Model.SortAscending)
  8.             htmlAttributes.Add("class", "sortAsc");
  9.         else
  10.             htmlAttributes.Add("class", "sortDesc");
  11.     }
  12. %>
  13.  
  14. <%= Html.ActionLink(
  15.                         ViewData["DisplayName"].ToString(),                             // Texto de enlace
  16.                         Html.ViewContext.RouteData.Values["action"].ToString(),         // Acción
  17.                         Html.ViewContext.RouteData.Values["controller"].ToString(),     // Contrlador
  18.                         routeData,                                                      // Ruta
  19.                             htmlAttributes                                              // Atributos HTML para aplicar a los hipervinculos
  20.                    )
  21. %>

Por último añadimos las clases que vamos a aplicar para personalizar el formato de orden dentro de la hoja de estilos Site.css, y que será el siguiente:

  1. /* Sorting UI */
  2. a.sortAsc {
  3.     padding-right: 16px;
  4.     background-image: url(arrow_up.png);
  5.     background-position: right;
  6.     background-repeat: no-repeat;
  7. }
  8.  
  9. a.sortDesc {
  10.     padding-right: 16px;
  11.     background-image: url(arrow_down.png);
  12.     background-position: right;
  13.     background-repeat: no-repeat;
  14. }

Si compilamos la aplicación y navegamos a la ruta /Store/Sortable, vemos como obtenemos todo el listado de elementos de nuestro modelo tal y como esta definido. Y si seleccionamos la flecha, o cualquiera de los títulos de la tabla podemos cambiar el valor de los parámetros de orden.

imageimage

[ASP.NET MVC] Mostrar datos paginados en un Datagrid I

Siguiendo la línea de Scott Mitchell en sus últimos artículos, a lo largo de una serie de posts vamos a ir viendo como podemos mostrar nuestros datos en formato datagrid, y como podemos aplicar las funcionalidades de paginado, filtrado y orden sobre los mismos, dentro de una aplicación ASP.NET MVC. En el artículo de hoy vamos a ver el primero de los casos, para ello creamos un proyecto de tipo ASP.NET MVC 2 Web Application que llamaremos DataGridApp. En el, añadimos la base de datos AdventureWorks, ya que tiene un gran número de tablas y registros con los que poder trabajar, y aplicarles la funcionalidad específica.

A continuación creamos un modelo de tipo LINQ to SQL Classes llamado StoreModel, que contendrá las tablas Customer y Store, de forma que nuestro modelo quede de la siguiente forma:

image

 

A continuación creamos un controlador especifico para trabajar con el modelo creado, y al que llamaremos StoreController.

image

En dicho controlador

1.Añadimos la referencia a nuestro modelo, y la referencia a Linq para poder trabajar con ellos:

  1. using DataGridApp.Models;
  2. using System.Data.Linq;

2.Inicializamos el contexto de nuestro modelo:

  1. StoreModelDataContext context = new StoreModelDataContext();

 

3.Creamos la acción Index, cuyo código será el siguiente:

  1. public ActionResult Index()
  2. {
  3.     return View(context.Stores);
  4. }

Y su vista correspondiente, de forma que podamos mostrar todos los elementos definidos en nuestro contexto de datos. Para ello hacemos clic con él botón derecho sobre Index y añadimos una vista con las siguientes propiedades:

image

En dicha vista, cambiamos la propiedad Inherits a:

System.Web.Mvc.ViewPage<IEnumerable<DataGridApp.Models.Store>>

Y dentro del contenedor principal añadimos el siguiente código:

  1. <table>
  2.         <tr>
  3.             <th>Store Name</th>
  4.             <th>Cust. Acc. Number</th>
  5.             <th>Sales Person ID</th>
  6.         </tr>
  7.  
  8.     <% foreach (var item in Model) { %>
  9.     
  10.         <tr>
  11.             <td><%: item.Name %></td>
  12.             <td><%: item.Customer.AccountNumber %></td>
  13.             <td><%: item.SalesPersonID %></td>
  14.         </tr>
  15.     
  16.     <% } %>
  17.  
  18.     </table>

A continuación creamos una clase para definir el modelo específico que queremos mostrar mediante la acción Paged. Para ello hacemos clic con el botón derecho sobre la carpeta Models y añadimos una clase llamada StoreGridModel que contendrá el siguiente código:

  1. public class StoreGridModel
  2.     {
  3.  
  4.         // Definimos las propiedades
  5.         public IEnumerable<Store> Stores { get; set; }
  6.         public int CurrentPageIndex { get; set; }
  7.         public int PageSize { get; set; }
  8.         public int TotalRecordCount { get; set; }
  9.         public int NumericPageCount { get; set; }
  10.         public int PageCount
  11.         {
  12.             get
  13.             {
  14.                 return this.TotalRecordCount / this.PageSize;
  15.             }
  16.         }
  17.         public StoreGridModel()
  18.         {
  19.  
  20.             // Establecemos los valores por defecto
  21.             this.PageSize = 10;
  22.             this.NumericPageCount = 10;
  23.         }
  24.  
  25.     }

Por último pasamos a definir la acción Paged, a través de la cual mostraremos nuestros elementos paginados en grupos de 10 en 10, y por los que podremos navegar mediante el uso de vistas parciales. El código que utilizamos para implementar dicha acción es el siguiente:

  1. public ActionResult Paged(int page = 1, int pageSize = 10)
  2.         {
  3.             var model = new  StoreGridModel
  4.             {
  5.                 CurrentPageIndex = page,
  6.                 PageSize = pageSize
  7.             };
  8.  
  9.             // Se determina el numero total de «stores» que se muestran en cada página
  10.             model.TotalRecordCount = context.Stores.Count();
  11.             // Se obtiene la página actual de «stores»
  12.  
  13.             model.Stores = context.Stores
  14.                            .Skip((model.CurrentPageIndex – 1) * model.PageSize)
  15.                            .Take(model.PageSize);
  16.  
  17.             return View(model);
  18.         }

 

Definimos su vista correspondiente, que tendrá las siguientes propiedades:

image

Y cuyo código será el siguiente:

  1. <p>
  2.         <i>Estas viendo la página <%: Model.CurrentPageIndex %> de <%: Model.PageCount %></i>
  3.     </p>
  4.     <table>
  5.         <tr>
  6.             <th>Store Name</th>
  7.             <th>Cust. Acc. Number</th>
  8.             <th>Sales Person ID</th>
  9.         </tr>
  10.  
  11.     <% foreach (var item in Model.Stores) { %>
  12.     
  13.         <tr>
  14.             <td><%: item.Name %></td>
  15.             <td><%: item.Customer.AccountNumber %></td>
  16.             <td><%: item.SalesPersonID %></td>
  17.         </tr>
  18.     
  19.     <% } %>
  20.  
  21.         <tr>
  22.             <td colspan=»5″>
  23.                 <% Html.RenderPartial(«Pager», Model); %>
  24.             </td>
  25.         </tr>
  26.  
  27.     </table>

Para que dicha vista funcione correctamente es necesario implementar previamente dos vistas parciales que guardaremos en la carpeta Views/Shared.

La primera de ellas, Pager.ascx nos permitirá realizar el renderizado parcial de los elementos de nuestro modelo, y en ella definiremos los distintos elementos de navegación. Esta vista se crea con las siguientes propiedades:

image

Y cuyo código es el siguiente:

  1. <div class=»pager»>
  2. <%
  3.     // Creamos el Link «Anterior»
  4.     Html.RenderPartial(«PagerLink», Model, new ViewDataDictionary { { «Text», «<< Anterior» }, { «PageIndex», Model.CurrentPageIndex – 1 }, { «Selected», false }, { «Inactive», Model.CurrentPageIndex == 1 } });
  5.  
  6.     
  7.     // Creamos los links númericos    
  8.     var startPageIndex = Math.Max(1, Model.CurrentPageIndex – Model.NumericPageCount / 2);
  9.     var endPageIndex = Math.Min(Model.PageCount, Model.CurrentPageIndex + Model.NumericPageCount / 2);
  10.  
  11.     // Mostramos los valores iniciales
  12.     if (Model.PageCount > Model.NumericPageCount / 2)
  13.     {
  14.         if (startPageIndex > 1)
  15.             Html.RenderPartial(«PagerLink», Model, new ViewDataDictionary { { «Text», «1» }, { «PageIndex», 1 }, { «Selected», false }, { «Inactive», false } });
  16.         if (startPageIndex > 2)
  17.             Html.RenderPartial(«PagerLink», Model, new ViewDataDictionary { { «Text», «2» }, { «PageIndex», 2 }, { «Selected», false }, { «Inactive», false } });
  18.         if (startPageIndex > 3)
  19.             Html.RenderPartial(«PagerLink», Model, new ViewDataDictionary { { «Text», «…» }, { «PageIndex», 1 }, { «Selected», false }, { «Inactive», true } });
  20.     }
  21.  
  22.     // Añadimos el resto de páginas numericas
  23.     for (var i = startPageIndex; i <= endPageIndex; i++)
  24.         Html.RenderPartial(«PagerLink», Model, new ViewDataDictionary { { «Text», i }, { «PageIndex», i }, { «Selected», i == Model.CurrentPageIndex }, { «Inactive», false } });
  25.  
  26.     
  27.     // Mostramos los valores finales
  28.     if (Model.PageCount > Model.NumericPageCount / 2)
  29.     {
  30.         if (endPageIndex < Model.PageCount – 2)
  31.             Html.RenderPartial(«PagerLink», Model, new ViewDataDictionary { { «Text», «…» }, { «PageIndex», 1 }, { «Selected», false }, { «Inactive», true } });
  32.         if (endPageIndex < Model.PageCount – 1)
  33.             Html.RenderPartial(«PagerLink», Model, new ViewDataDictionary { { «Text», Model.PageCount – 1 }, { «PageIndex», Model.PageCount – 1 }, { «Selected», false }, { «Inactive», false } });
  34.         if (endPageIndex < Model.PageCount)
  35.             Html.RenderPartial(«PagerLink», Model, new ViewDataDictionary { { «Text», Model.PageCount }, { «PageIndex», Model.PageCount }, { «Selected», false }, { «Inactive», false } });
  36.     }
  37.  
  38.     // Creamos el link «Siguiente»
  39.     Html.RenderPartial(«PagerLink», Model, new ViewDataDictionary { { «Text», «Siguiente >>» }, { «PageIndex», Model.CurrentPageIndex + 1 }, { «Selected», false }, { «Inactive», Model.CurrentPageIndex == Model.PageCount } });
  40. %>
  41. </div>

Y la segunda PagerLink.ascx que nos permitirá navegar por las distintas vistas, y aplicarles los estilos específicos. Esta contará con las siguientes propiedades:

image

Y su código es el siguiente:

  1. <%
  2.     if ((bool)ViewData[«Inactive»])
  3.     {
  4.         Response.Write(string.Format(«<span class=»{0}»>{1}</span>», «pagerButtonDisabled», ViewData[«Text»]));
  5.     }
  6.     else
  7.     {
  8.         var routeData = new RouteValueDictionary { { «page», ViewData[«PageIndex»].ToString() }, { «pageSize», Model.PageSize } };
  9.         var htmlAttributes = new Dictionary<string, object>();
  10.         if ((bool)ViewData[«Selected»])
  11.             htmlAttributes.Add(«class», «pagerButtonCurrentPage»);
  12.         else
  13.             htmlAttributes.Add(«class», «pagerButton»);
  14.  
  15.         Response.Write(
  16.             Html.ActionLink(
  17.                     ViewData[«Text»].ToString(),                                    // Link Text
  18.                     Html.ViewContext.RouteData.Values[«action»].ToString(),         // Acción
  19.                     Html.ViewContext.RouteData.Values[«controller»].ToString(),     // Controlador
  20.                     routeData,                                                      // Ruta
  21.                     htmlAttributes                                                  // Atributos HTML para aplicar a los hipervinculos
  22.                 ).ToHtmlString()
  23.         );
  24.     }
  25. %>

Creamos las clases que vamos a aplicar para personalizar el formato del paginado dentro de la hoja de estilos Site.css:

  1. /*Customizations*/
  2.  
  3. td.pager {
  4.     background-color: #eee;
  5. }
  6.  
  7. td.pager div {
  8.     font-size: smaller;
  9.     font-weight: bold;
  10.     padding: 5px;
  11. }
  12.  
  13. a.pagerButton, a.pagerButton:visited
  14. {
  15.     border: solid 1px #eee;
  16.     padding: 4px;
  17.     text-decoration: none;
  18.     color: #006;
  19.     margin: 0px 2px 0px 2px;
  20. }
  21.  
  22. a.pagerButton:hover
  23. {
  24.     border: solid 1px black;
  25.     color: Black;
  26. }
  27.  
  28. a.pagerButtonCurrentPage
  29. {
  30.     border: solid 1px #00a;
  31.     padding: 4px;
  32.     text-decoration: none;
  33.     color: White;
  34.     background-color: #006;
  35.     margin: 0px 2px 0px 2px;
  36. }
  37.  
  38. .pagerButtonDisabled
  39. {
  40.     border: none;
  41.     color: #999;
  42.     padding: 4px;
  43. }

Finalmente, compilamos la aplicación y navegamos a la ruta /Store/Paged donde podemos ver como se obtienen todos los elementos de nuestro modelo estructurados tal y como lo hemos definido en su configuración y como podemos navegar por ellos de forma sencilla.

image

[ASP.NET MVC] Validación modelo Entity Framework

En este artículo vamos a ver como validar los elementos de nuestro modelo de datos cuando implementamos las acciones Create y Edit. Existen dos tipos de validación que se pueden aplicar; validaciones en el lado servidor, y en el lado cliente. Para el primero de los casos vamos a usar Data Annotations. Las Data Annotations son atributos que nos permiten describir reglas que deben cumplir las propiedades de nuestro modelo:

  • Required – Indica que la propiedad es un campo requerido
  • DisplayName – Define el texto que queremos mostrar al validar los campos
  • StringLength – Define la longitud máxima para un campo de tipo string
  • Range – Da un rango de valores(máximo y mínimo) para un campo numérico
  • Bind – Permite excluir o incluir campos cuando enlazamos parámetros o valores con las propiedades del modelo
  • ScaffoldColumn – Permite ocultar campos del formulario de edición

 

En este caso partimos de una aplicación de tipo ASP.NET MVC 2 Web Application llamada ValidationApp, que tiene una base de datos que consta de una tabla llamada Team cuyos campos son los siguientes:

image

Creamos el modelo que nos permita interactuar con dicha base de datos que será de tipo ADO.NET Entity  Data Model, y que llamaremos TeamModel. Cuando trabajamos con modelos de tipo Entity Framework, las clases de dichos modelos se autogeneran, por lo que si añadimos las validaciones directamente sobre ellas, estas se sobrescribirían al actualizar el modelo desde la base de datos. En este caso lo que hacemos es definir clases parciales de nuestros modelos, en los que configuraremos las validaciones mediante el uso del atributo [MetadataType].

Creamos una clase llamada Team.cs que nos permitirá validar los valores de nuestro modelo Team. Para ello hacemos clic con el botón derecho sobre la carpeta Models y seleccionamos Add > class. Dentro de dicha clase:

1. Añadimos las siguientes referencias:

  1. using System.ComponentModel;
  2. using System.ComponentModel.DataAnnotations;
  3. using System.Web.Mvc;

3. Creamos la clase de validación de los metadatos con los requisitos de validación para cada uno de los campos

  1. [Bind(Exclude=«TeamID»)]
  2.         public class TeamMetaData
  3.         {
  4.             [Required(ErrorMessage=«Introduce el nombre del equipo»)]
  5.             public object TeamName { get; set; }
  6.  
  7.             [DisplayName(«Nombre del pabellón de juego»)]
  8.             [Required(ErrorMessage = «Introduce el nombre del pabellón»)]
  9.             public object TeamPlace { get; set; }
  10.  
  11.             [DisplayName(«Nombre del equipo»)]
  12.             [Required(ErrorMessage = «Introduce el nombre de la ciudad»)]
  13.             [StringLength(50,ErrorMessage=«El número de carácteres es demasiado largo»)]
  14.             public object TeamCity { get; set; }
  15.  
  16.         }

3. Indicamos que tipo de metadatos vamos a utilizar:

  1. [MetadataType(typeof(TeamMetaData))]

De forma que el código completo de la clase queda de la siguiente forma:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Web;
  5. using System.ComponentModel;
  6. using System.ComponentModel.DataAnnotations;
  7. using System.Web.Mvc;
  8.  
  9.  
  10. namespace ValidationApp.Models
  11. {
  12.     [MetadataType(typeof(TeamMetaData))]
  13.     public partial class Team
  14.     {
  15.         [Bind(Exclude=«TeamID»)]
  16.         public class TeamMetaData
  17.         {
  18.             [Required(ErrorMessage=«Introduce el nombre del equipo»)]
  19.             public object TeamName { get; set; }
  20.  
  21.             [DisplayName(«Nombre del pabellón de juego»)]
  22.             [Required(ErrorMessage = «Introduce el nombre del pabellón»)]
  23.             public object TeamPlace { get; set; }
  24.  
  25.             [DisplayName(«Nombre del equipo»)]
  26.             [Required(ErrorMessage = «Introduce el nombre de la ciudad»)]
  27.             [StringLength(50,ErrorMessage=«El número de carácteres es demasiado largo»)]
  28.             public object TeamCity { get; set; }
  29.  
  30.         }
  31.     }
  32. }

A continuación creamos las distintas acciones de nuestro controlador y sus vistas correspondientes, como ya hemos visto en otras ocasiones, de forma que podamos implementar operaciones de tipo CRUD con nuestra base de datos.

Finalmente compilamos nuestra aplicación y navegamos hasta /Team/Create. Si intentamos crear un elemento, y este no cumple con los criterios establecidos vemos como se aplican las reglas de validación correspondientes.

image image image

Aunque la el tipo de validación que hemos visto funciona correctamente, existen tres problemas con él:

1. El usuario tiene que esperar a que se envíe el formulario, se valide en el servidor, y se reciba la respuesta en el navegador.

2. El usuario no obtiene inmediatamente feedback cuando se corrige un campo

3. Se gastan recursos de servidor para representar la validación lógica

A continuación vamos a ver como se realizaría la validación en el lado cliente, y las ventajas que esta presenta. Como nuestra clase Team, ya tiene los atributos de validación establecidos, sólo necesitamos añadir algo de javascript a nuestra página Create.aspx para activar la validación en el lado cliente. Para ello abrimos la vista Team/Create.aspx.

Arrastramos a dicha vista los archivos: MicrosoftAjax.js, MicrosoftMvcAjax.js, MicrosoftMvcValidation.js

De forma que se genera el siguiente código:

  1. <script src=»../../Scripts/MicrosoftAjax.js» type=»text/javascript»></script>
  2. <script src=»../../Scripts/MicrosoftMvcAjax.js» type=»text/javascript»></script>
  3. <script src=»../../Scripts/MicrosoftMvcValidation.js» type=»text/javascript»></script>

A continuación añadimos la siguiente línea de código sobre Html.BeginForm:

  1. <%Html.EnableClientValidation();%>

Así quedaría el código de nuestra vista:

  1. <asp:Content ID=»Content2″ ContentPlaceHolderID=»MainContent» runat=»server»>
  2.  
  3.     <h2>Create</h2>
  4.     <script src=»../../Scripts/MicrosoftAjax.js» type=»text/javascript»></script>
  5.     <script src=»../../Scripts/MicrosoftMvcAjax.js» type=»text/javascript»></script>
  6.     <script src=»../../Scripts/MicrosoftMvcValidation.js» type=»text/javascript»></script>
  7.     
  8.     <%Html.EnableClientValidation();%>
  9.     <% using (Html.BeginForm()) {%>
  10.         <%: Html.ValidationSummary(true) %>
  11.  
  12.         <fieldset>
  13.             <legend>Fields</legend>
  14.             
  15.             <div class=»editor-label»>
  16.                 <%: Html.LabelFor(model => model.TeamID) %>
  17.             </div>
  18.             <div class=»editor-field»>
  19.                 <%: Html.TextBoxFor(model => model.TeamID) %>
  20.                 <%: Html.ValidationMessageFor(model => model.TeamID) %>
  21.             </div>
  22.             
  23.             <div class=»editor-label»>
  24.                 <%: Html.LabelFor(model => model.TeamName) %>
  25.             </div>
  26.             <div class=»editor-field»>
  27.                 <%: Html.TextBoxFor(model => model.TeamName) %>
  28.                 <%: Html.ValidationMessageFor(model => model.TeamName) %>
  29.             </div>
  30.             
  31.             <div class=»editor-label»>
  32.                 <%: Html.LabelFor(model => model.TeamPlace) %>
  33.             </div>
  34.             <div class=»editor-field»>
  35.                 <%: Html.TextBoxFor(model => model.TeamPlace) %>
  36.                 <%: Html.ValidationMessageFor(model => model.TeamPlace) %>
  37.             </div>
  38.             
  39.             <div class=»editor-label»>
  40.                 <%: Html.LabelFor(model => model.TeamCity) %>
  41.             </div>
  42.             <div class=»editor-field»>
  43.                 <%: Html.TextBoxFor(model => model.TeamCity) %>
  44.                 <%: Html.ValidationMessageFor(model => model.TeamCity) %>
  45.             </div>
  46.             
  47.             <p>
  48.                 <input type=»submit» value=»Create» />
  49.             </p>
  50.         </fieldset>
  51.  
  52.     <% } %>
  53.  
  54.     <div>
  55.         <%: Html.ActionLink(«Back to List», «Index») %>
  56.     </div>
  57.  
  58. </asp:Content>

Si ejecutamos la aplicación y accedemos a la página /Team/Create, vemos que los mensajes de error aparecen inmediatamente sin tener que esperar a los postbacks. Esto se debe a que ahora la validación se realiza inicialmente en el cliente (mediante el uso de javascript).

Esto es todo lo que necesitamos aplicar para establecer una validación en el lado cliente. En este caso todos los campos son chequeados en el navegador antes de enviar la información a las acciones del controlador, y la respuesta de validación es inmediata.