Y el combate se decidió por KO (iv): Choque de viewmodels

Disclaimer: Este post es un poco distinto al resto de posts de esta serie sobre knockout. De hecho no tenía presente escribirlo pero lo he hecho cuando he visto la cantidad de preguntas relativas a ello que aparecen por Google. Aunque lo he reescrito varias veces, entiendo que puede ser un post durillo de leer, especialmente si no se tiene experiencia previa en ASP.NET MVC. Si NO quieres leerte este post, no te preocupes: no es necesario para nada para entender el resto de posts de la serie, ni explica nada nuevo sobre knockout que no hayamos vistos en los 3 anteriores. Se trata simplemente de divagaciones sobre el siguiente tema: ¿Se pueden usar los helpers de ASP.NET MVC para mostrar o editar datos junto con knockout?

En los tres primeros posts de esta serie sobre Knockout hemos estado viendo algunos de los aspecto básicos de dicha librería (en sucesivos posts iremos viendo más). Pero hasta ahora nos hemos limitado a un modelo de aplicación en el que la vista obtenía los datos llamando a un servicio REST, y a partir de dichos datos creaba su viewmodel y usaba knockout para representarlo en pantalla.

Pero este modelo, extremadamente útil si hacemos uso intensivo de Ajax, no es el único posible. Existe otro, más “clásico” en el que las vistas reciben datos de sus controladores (en lugar de tener que realizar una llamada ajax a un servicio REST). En el contexto de ASP.NET MVC llamamos también viewmodel a los datos que un controlador manda a la vista (y que esta accede a través de su propiedad Model). Así pues ahora vamos a lidiar con dos viewmodels:

  1. Los datos que el controlador manda a la vista y que esta accede usando la propiedad Model. Es el viewmodel de ASP.NET MVC o el viewmodel de servidor.
  2. El objeto javascript que usa knockout. Es el viewmodel de cliente.

En este post veremos como el “modelo clásico de ASP.NET MVC” choca con el modelo knockout y daremos algunas indicaciones de como podemos solucionarlo. No llegaremos a una solución completa porque simple y llanamente no la hay.

Escenario 1. Mostrando datos

Vamos a suponer que tenemos una clase Beer:

   1: public class Beer

   2: {

   3:     public string Name { get; set; }

   4:     public int Ibu { get; set; }

   5: }

Y luego tenemos un controlador con una acción tal que:

   1: public ActionResult Index()

   2: {

   3:     var beers = new List<Beer>

   4:                     {

   5:                         new Beer { Name = "Estrella Damm", Ibu = 30},

   6:                         new Beer { Name = "Marina Devil's IPA", Ibu = 150}

   7:                     };

   8:     return View(beers);

   9: }

La vista recibe esta lista de cervezas en su propiedad Model. Ahora la pregunta es, podemos usar knockout para mostrar los datos?

La respuesta es que sí, aunque para ello debemos crear el viewmodel de cliente a partir de los datos del viewmodel de servidor:

   1: @using System.Web.Script.Serialization

   2: @model IEnumerable<MvcApplication1.Models.Beer>

   3: @{

   4:     ViewBag.Title = "Index";

   5:     var ser = new JavaScriptSerializer();

   6:     var jscode = Model != null ? ser.Serialize(Model) : string.Empty;

   7: }

   8:  

   9: <h2>Index</h2>

  10:  

  11: <script type="text/javascript">

  12:     $(document).ready(function () {

  13:         var vm = JSON.parse('@Html.Raw(jscode)');

  14:         vm = completeViewModel(vm);

  15:         ko.applyBindings(vm);

  16:     });

  17:  

  18:     function completeViewModel(vm) {

  19:         return { items: vm };

  20:     }

  21: </script>

La vista contiene un bloque de código Razor y luego un tag <script> con código de cliente:

  1. El código Razor recoje el ViewModel de ASP.NET MVC y lo serializa a una cadena JSON.
  2. El código <script /> usa JSON.parse para obtener un objeto javascript a  partir de la cadena en JSON obtenida en el código anterior.

Si ejecutamos esta vista y ponemos un breakpoint en el javascript cuando se ha obtenido el viewmodel de cliente (yo he usado las herramientas de Chrome para ello), vemos que efectivamente tenemos un objeto javascript con los datos del viewmodel de ASP.NET MVC (excepto que le he añadido la propiedad items que me servirá para el enlace con knockout):

image

Finalmente tan solo nos queda mostrar los datos:

   1: <div id="beers" data-bind="foreach: items">

   2:     <span data-bind="text: Name"></span> - <span data-bind="text: Ibu"></span>

   3:     <br />

   4: </div>

En este punto es necesario hacer una mención importante: la cadena JSON resultado de convertir a JSON el viewmodel de ASP.NET MVC se está incluyendo en el código fuente de la página (a través del Html.Raw). Eso es lo que obtengo si hago un “ver codigo fuente”:

   1: var vm = JSON.parse('[{"Name":"Estrella Damm","Ibu":30},{"Name":"Marina Devilu0027s IPA","Ibu":150}]');

Por lo tanto, si mi viewmodel ASP.NET MVC es grande eso puede generar páginas grandes (en Kilobytes) y por lo tanto lentas.

Pregunta: Puedo usar los helpers de ASP.NET MVC (DisplayFor y similares) para mostrar estos datos?

Respuesta: Si… y no. Me explico.

Algunos helpers pueden no generar tag html. Por ejemplo DisplayFor para una propiedad de tipo string, no genera tag alguno (simplemente renderiza el texto tal cual). Si no hay tag HTML, no hay sitio donde poner el atributo data-bind para usar con knockout.

Si usas un helper que genere un tag (p.ej. pones un Html.TextboxFor), entonces puedes usarlo, pero ten presente que el propio helper ya genera el atributo value, así que realmente el enlace con knockout no tiene mucho sentido, si tan solo vas a mostrar datos.

Mi recomendación es que te olvides de los helpers de MVC si vas a mostrar datos usando knockout. Si de todos modos quieres usarlos debes hacer que dichos helpers generen el atributo data-bind para mostrar los datos usando knockout. P.ej. el siguiente código generaria un textbox con el atributo data-bind=”value: Name” y de solo lectura:

   1: @Html.TextBoxFor(x => x.Name, new { data_bind = "value: Name", @readonly = true })

(Fíjate en que como data-bind no es un nombre válido para una propiedad del objeto anónimo en C# se usa data_bind (ASP.NET MVC transforma el guión bajo en un guión al generar el código), y el uso de @readonly en lugar de readonly (que es palabra clave reservada de C#)).

No obstante… crees que tiene realmente sentido hacer esto? Ya tienes tus datos en el viewmodel de knockout, es mucho mejor usar los mecanismos de knockout para mostrarlos!

Escenario 2: Edición

Empecemos por hacer que nuestro controlador devuelva una sola cerveza y vamos a intentar editarla usando knockout. Ni corto ni perzoso he creado la acción nueva (Edit):

   1: public ActionResult Edit()

   2: {

   3:     var beer = new Beer() {Name = "Mezquita", Ibu = 50};

   4:     return View(beer);

   5: }

Bien, ahora vamos a crear una vista de edición, pero usando knockout, para ello usando el esquema anterior, obtenemos el viewmodel de knockout a partir del viewmodel de ASP.NET MVC:

   1: @using System.Web.Script.Serialization

   2: @model MvcApplication1.Models.Beer

   3: @{

   4:     ViewBag.Title = "Index";

   5:     var ser = new JavaScriptSerializer();

   6:     var jscode = Model != null ? ser.Serialize(Model) : string.Empty;

   7: }

   8:  

   9: <script type="text/javascript">

  10:     $(document).ready(function () {

  11:         var vm = JSON.parse('@Html.Raw(jscode)');

  12:         ko.applyBindings(vm);

  13:     });

  14:  

  15: </script>

Bien, ahora viene el siguiente punto. Intentemos usar los helpers de ASP.NET MVC para crear los controles de edición:

   1: @Html.LabelFor(x=>x.Name)

   2: @Html.TextBoxFor(x=>x.Name, new {data_bind="value: Name"})

   3: <br />

   4: @Html.LabelFor(x=>x.Ibu)

   5: @Html.TextBoxFor(x=>x.Ibu, new {data_bind="value: Ibu"})

Fijaos ya en el “primer choque”. A pesar de usar las versiones “strong typed” de los editores, tengo que especificar de nuevo el nombre de la propiedad como valor de data_bind. Eso es proclive a errores, ya que puedo hacer TextBoxFor(x=>x.Name) y en el data_bind poner otro nombre de propiedad. Por supuesto esto se podria arreglar con un helper propio.

Al margen de este detalle, parece que todo funciona. Incluso si envio el formulario me llegan los datos modificados:

image

Pero… reflexionemos. Hemos usado knockout para algo? Pues no. De hecho puedes comprobarlo comentando la línea ko.applyBindings y verás que todo sigue funcionando! Eso es porque los helpers generan el atributo value de los controles que ya establece su valor inicial. Luego una vez se envía el formulario entra en acción el ModelBinder que rellena el viewmodel de ASP.NET MVC a partir de los datos del POST. Knockout no hace nada ahí.

Así pues si nos limitamos a enviar (submit) los datos introducidos en unos campos de texto que están dentro de un formulario, knockout no pinta nada. El binding de knockout es en cliente. Knockout lo usaríamos si “antes” de enviar los datos queremos hacer algos con ellos en cliente. Así pues vamos a probar de hacer algo con los datos en cliente.

P.ej. mostrarlos (con un alert). Para ello asignamos un evento javascript en el submit del formulario:

   1: $("form").submit(function(evt) {

   2:     alert(vm.Name + " " + vm.Ibu);

   3: });

Y lo probamos… ¿Creéis que funcionará? La respuesta en la imagen siguiente:

image

Como era de esperar, funciona porque konockout modifica el viewmodel a partir de los datos de los controles. Recuerda que luego cuando enviamos el formulario, enviamos los datos que están en los controles (no usamos el viewmodel de knockout para nada). ¡Pero ahora sabemos que los tenemos sincronizados!

Ahora hagamos una cosilla… En el textbox de Ibu (que es un int) introduzcamos algo que no sea numérico, p. ej. “Pepe”. Y eso es lo que obtenemos de vuelta:

image

¿Qué os parece? ¿Os gusta? A mi no. ¿Por que no me gusta esto?

Pues muy fácil: he perdido el valor incorrecto que había entrado. Y es que ese es un comportamiento de los helpers de asp.net mvc: si entro algún valor incorrecto se preserva. Si queréis hacer la prueba basta con comentar la línea ko.applyBindings y lo veréis:

image

Este comportamiento es por diseño y es propio de los helpers de edición. ¿Como es que al usar knockout perdemos el valor “pepe” incorrecto y se nos sustituye por un 0? Pues muy sencillo:

  1. El helper genera el código de forma correcta, con el valor del atributo value a “pepe”.
  2. El viewmodel de ASP.NET MVC pasado a la vista tiene un 0 en la propiedad Ibu (no hay manera de convertir “pepe” a int, así que el ModelBinder no hace nada (y el valor por defecto de un int es 0) y deja un error en el ModelState).
  3. Al crear el viewmodel de knockout lo creamos a partir del viewmodel de ASP.NET MVC que tiene un 0 en la propiedad Ibu.
  4. Al llamar a ko.applyBindings() modificamos el valor inicial del control (que era “pepe”) por el valor del viewmodel de knockout (que es 0).

Por lo tanto “perdemos” esta capacidad de los helpers de mantener el dato incorrecto entrado (si este no era asignable al viewmodel de ASP.NET MVC).

Si has leído los posts anteriores de esta serie, probablemente habrás levantado una ceja cuando creábamos el viewmodel de knockout a partir de la cadena json obtenida de serializar el viewmodel de ASP.NET MVC. Por qué? Bueno… modifiquemos la vista para que el titulo sea así:

   1: <legend>Editando: <span data-bind="text: Name"></span></legend>

Ahora la idea es que al modificar el textbox que contiene el nombre de la cerveza, se modifique el título, pero eso no ocurre:

image

He modificado el textbox pero en el título sigue apareciendo el nombre anterior. La razón? Pues muy sencillo, el viewmodel de knockout es un objeto javascript plano. No tiene definido ningún observable.

Es posible crear observables de forma relativamente sencilla, a partir del viewmodel de ASP.NET MVC? La respuesta es que sí, pero para ello debemos usar un plugin de knockout (knockout mappings). No lo veremos en este post (sí en alguno futuro), quedaros con la idea de que se puede hacer de forma “relativamente” sencilla.

Resumiendo: en ediciones simples (de un solo elemento simultaneo) podemos usar los helpers de asp.net mvc, aunque con las salvedades vistas en este punto. Tu mismo debes decidir si te compensa o no usarlos.

Mi opinión: Los helpers de ASP.NET MVC existen para dar solución a un problema concreto. La verdad es que knockout, realmente, da solución al mismo problema pero lo hace desde una aproximación radicalmente distinta. Intentar aprovechar los helpers de ASP.NET MVC junto con knockout es posible, pero no es algo que yo haría. Recuerda que los helpers están para ayudar, en ningún caso estamos obligados a usarlos!

Si no te sientes a gusto creando “a mano” controles HTML (¡deberías sentirte a gusto con ello!) y quieres una “aproximación tipo helpers MVC” para knockout pues la solución pasa por implementarte tus propios helpers…

Bueno… dejemos de divagar por hoy!

Un saludo!!!!

Y el combate se decidió por KO (iii)

Bueno, continuamos aquí nuestra serie explorando las maravillas de Knockout. Todos los posts de esta serie los podéis encontrar en: http://geeks.ms/blogs/etomas/archive/tags/knockout/default.aspx

Serializando viewmodels

En el post anterior, vimos los observables de knockout y como funcionaban. Vimos como crear un formulario, enlazarlo a un viewmodel que usara observables y como mandar el viewmodel serializado en json hacia un servicio REST.

Ciertamente, el tema de la serialización a JSON de nuestro viewmodel era un poco peliagudo. Dado que los observables son funciones debíamos “crear” un objeto adicional a partir de nuestro viewmodel, invocando a los observables de forma manual, ya que usar JSON.stringify sobre nuestro viewmodel no funcionaba (los observables no se serializaban). El código que usábamos era:

   1: var jsonBeer = JSON.stringify({

   2:    Name : this.Name(),

   3:    Ibu : this.Ibu(),

   4:    Abv : this.Abv()

   5: });

Tener que crear otro objeto “con la misma” estructura que nuestro viewmodel e ir llamando manualmente a los observables es posible para viewmodels sencillitos como este, pero para viewmodels más grandes es, como mínimo, un peñazo.

Como ya debes estar pensando, knockout ofrece una solución a ese problema, ya que como comentamos en el post anterior enviar objetos via JSON es bastante común hoy en día. Si vuestro viewmodel contiene observables entonces la mejor manera de serializarlos es usar uno de los métodos siguientes:

  1. ko.toJS: Convierte el viewmodel a un objeto “plano” javascript (es decir hace justo lo que hemos hecho nosotros a mano, es decir invocar los observables). El objeto devuelto puede serializarse a JSON de la forma que se prefiera.
  2. ko.toJSON: Llama a ko.toJS y serializa el objeto usando JSON.stringify.

Así, en lugar del código anterior podemos usar tranquilamente:

   1: var jsonBeer = ko.toJSON(this);

Para la deserialización (es decir, obtención del objeto viewmodel a partir de los datos JSON), Knockout no trae de serie nada. Esto implica que si nuestro viewmodel usa observables debemos manualmente crear nuestro viewmodel y llamar a los observables para inicializar el valor. Eso también puede ser un problema y para lidiar con ello existe un plugin de knockout. Hablaremos de él más adelante en esta serie de posts.

Observables calculados

Recuerda que los viewmodels están fuertemente atados a la vista. De hecho son la abstracción del modelo para una vista en particular y su tarea es “facilitar” al máximo el código de la vista.

Vamos a añadir en el formulario de modificación, el tipo de cerveza (si es una IPA, una stout o una pale ale, p. ej.). A nivel del servicio REST hemos de modificar la clase Beer:

   1: public class Beer

   2: {

   3:     public string Name { get; set; }

   4:     public decimal Abv { get; set; }

   5:     public int Ibu { get; set; }

   6:     public string Style { get; set; }

   7: }

Por el momento esto nos basta. Ahora, modificamos nuestra vista de Edicion (Edit.cshtml) para que tenga un campo adicional más donde entrar el tipo de cerveza:

   1: <label for="Name">Style</label>

   2: <input type="text" name="Name" id="Style" data-bind="value: Style" />

   3: <br />

Bien, fijaos en el uso del data-bind para enlazar este <input /> al valor de la propiedad Style del viewmodel.

Por supuesto debo modificar en la vista cuando creamos el viewmodel, para inicializar el observable Style, a partir de los datos JSON devueltos por el servicio:

   1: $.getJSON(uri, function(data) {

   2:     vm = {

   3:         Name  : ko.observable(data.Name),

   4:         Ibu  : ko.observable(data.Ibu),

   5:         Abv : ko.observable(data.Abv),

   6:         Style : ko.observable(data.Style),

   7:         editBeer : function() {

   8:     // Continua...

Finalmente modifico el método GetById del controlador ApiBeerController para que me informe del valor del campo Style:

   1: public Beer GetById(int id)

   2: {

   3:     return new Beer { Name = "Imperial Stout #" + id, Abv = 10.0M, Ibu = 120, Style="Imperial Stout"};

   4: }

Si ahora ejecuto obtengo lo esperado:

image

Hasta ahí no hemos hecho nada nuevo

Bien, ahora imaginad que queremos presentar en el título el nombre y el tipo de la cerveza, algo como “Heineken (American Lager)”. Una solución es componer esto en la vista. Para ello modifico el <h2>:

   1: <h2 id="title"></h2>

Y luego en el código javascript, cuando he cargado el viewmodel, justo antes (o después) de la llamada a ko.applyBindings coloco el siguiente código javascript:

   1: $("#title").text(vm.Name() + "(" + vm.Style() + ")");

El resultado es el esperado (se muestra el nombre y el tipo de cerveza dentro del <h2>. Pero… no os suena eso a un enlace? La función de un viewmodel es facilitar al máximo la creación de la vista, siendo una representación de lo que la vista muestra. No podría tener el viewmodel una propiedad que devolviese esta cadena? Es una propiedad especial cierto, ya que es de solo lectura y su valor está calculado a partir del valor de otras propiedades.

Esto en knockout se conoce como un observable calculado y se crean usando el método ko.computed. Este método recibe un parámetro que es la función que calcula el valor del observable:

   1: vm.Title = ko.computed(function() {

   2:     return vm.Name() + "(" + vm.Style() + ")";

   3: });

Aquí debo hacer un apunte importante sobre la sintaxis Javascript. Como quizá ya sabéis hay dos maneras en Javascript de inicializar objetos: usando un constructor o bien usando notación literal. Yo he usado en los ejemplos de esta serie he usado la notación literal (mientras que en los ejemplos de la propia web usan más la notación de constructor). Pues bien, los observables calculados son incompatibles con la notación literal. Eso significa que NO puedes declarar observables calculados a la vez que declaras el resto del viewmodel, y los tienes que añadir después. De hecho el código completo de la creación del viewmodel es:

   1: vm = {

   2:     Name  : ko.observable(data.Name),

   3:     Ibu  : ko.observable(data.Ibu),

   4:     Abv : ko.observable(data.Abv),

   5:     Style : ko.observable(data.Style),

   6:     editBeer : function() {

   7:        // Codigo...

   8:     }

   9: };

  10: vm.Title = ko.computed(function() {

  11:     return vm.Name() + "(" + vm.Style() + ")";

  12: });

Fijaos como las propiedades Name, Ibu, Abv, Style y editBeer son definidas en notación literal (propiedad : valor), pero el observable calculado lo he añadido luego.

Aclarado este aspecto “notacional”,  tan solo nos queda enlazar el <h2> con el valor del campo Title de nuestro viewmodel:

   1: <h2 id="title" data-bind="text: Title"></h2>

Y ahora podemos ver como todo funciona correctamente:

image

Pero recordad que los observables son “bidireccionales” no? Basta con modificar el textbox de Name o el de Style para que… ¡se cambie el título!

image

¿No os parece una pasada?

Bueno, lo dejamos aquí por hoy, en futuros posts seguiremos explorando las capacidades de knockout!

Saludos!

El problema de la WebGrid con VS2012RC y ASP.NET MVC4

Nota: Este post está basado en la versión RC de VS2012 y la versión RC de MVC4 y es posible (o eso espero, vaya!) que en la versión final no haya los problemas que este post menciona!

Buenas! Coje un VS2102RC y crea un nuevo proyecto ASP.NET MVC4, con la plantilla “Basic”.

Crea el HomeController, crea la acción Index y añádele un código tal como:

   1: public ActionResult Index()

   2: {

   3:     var data = new List<dynamic>() { new { Name = "Edu", Twitter = "eiximenis" } };

   4:     return View(data);

   5: }

Finalmente crea la vista Index.cshtml:

   1: @{

   2:     var grid = new WebGrid(Model, new [] {"Name", "Twitter"});

   3: }

   4: @grid.GetHtml()

Seguro que esperas ver una grid con las dos columnas y una fila no? ¡Pues no! Lo que verás es:

image

Obtendrás el error “CS0246: The type or namespace name ‘WebGrid’ could not be found (are you missing a using directive or an assembly reference?)”.

Si repites este mismo procedimiento pero cojes la plantilla “Empty” el código funcionará sin problemas 🙂

¿Y donde está el problema?

Pues eso me he estado preguntando un buen rato. Por supuesto antes de intentar hacer nada he usado el comodín de Google pero esta vez me ha fallado. La única referencia que he encontrado es un post en los blogs de ASP.NET (http://forums.asp.net/t/1823940.aspx/1?MVC4+WebGrid+problem+in+View+Razor+) donde alguien más experimenta el problema pero no se llega a ninguna solución.

Al final lo que he hecho para solucionar el problema ha sido:

  1. Desde NuGet desinstalar el paquete ASP.NET MVC4 RC 4.0.20505. Eso ha desinstalado también los paquetes Microsoft.AspNet.WebPages 2.0.20505.0 y Microsoft.AspNet.Razor 2.0.20505.0
  2. Volver a instalar el paquete ASP.NET MVC4 RC 4.0.20505 desde NuGet. Me ha dado un error y ha hecho un rollback.
  3. Intentar de nuevo volver a instalar el paquete ASP.NET MVC4 RC 4.0.20505. Ahora todo ha funcionado correctamente.

Después del tercer punto, la WebGrid ya funcionaba correctamente.

Honestamente desconozco la causa, pero bueno… si alguien se encuentra con ello, ya lo sabe. Que pruebe esto a ver si le funciona! 🙂

Un saludo!

Y el combate se decidió por KO (ii)

Como indica el título del post, ese es el segundo post de la serie que he empezado sobre knockout. Honestamente no sé cuantos posts habrá ni donde me (nos) llevará, pero espero que os sea útil!

En el post anterior (el primero) vimos un poco que era knockout y como mostrar datos devueltos a partir de un servicio REST implementado con WebApi. Ahora toca ir un poco más allá…

Formulario que te quiero formulario

La web está llena de formularios. Están en todos los sitios: para login y password, para darse de alta en un sitio, para solicitar información, para reservar tus vacaciones a Playa Bávaro… Los formularios son el mecanismo más sencillo para enviar información desde el cliente (navegador) al servidor. Usualmente (aunque ello no es obligatorio) se envían via POST, es decir conteniendo sus datos en el cuerpo de la petición, en lugar de GET donde los datos deben ir en la querystring. La razón es que los datos de un formulario pueden ser abritrariamente largos y podríamos tener URLs demasiado largas. Aunque realmente la especificación de http no impone un límite a las URLs (ver RFC2616 punto 3.2.1), también deja claro que no tienen porque ser “arbitrariamente largas”. Es decir, cada servidor puede establecer su tamaño máximo de URL y devolver un error 414 en caso de que la URL enviada por el user-agent sea demasiado larga. Y luego están los propios user-agents claro. Cada uno de ellos puede “unilateralmente” truncar la URL o rechazar peticiones si la URL excede cierto tamaño.

Cuando se envía un formulario via POST se usa por defecto el content-type application/x-www-form-urlencoded que básicamente significa codificar (en el cuerpo de la petición) todos los parámetros del formulario en la codificación clásica de nombre=valor&nombre2=valor2&

Es una práctica (relativamente) habitual cuando se crean APIs REST que si alguien tiene que enviar datos a dicha API (sea a través de POST o PUT p.ej.) no codifique dichos datos usando el content-type application/x-www-formurlencoded sino que codifique dichos datos usando algún otro formato (como p. ej. JSON). Esto es por desligarnos de “los formularios” (una API no trabaja con formularios, trabaja con datos) y también por coherencia: si mi API devuelve datos en JSON lo suyo es que los acepte también en este formato.

Bien, vamos a crear un formulario de edición. Pero esta vez no haremos un formulario normal, enviado via POST a través del content-type application/x-www-form-urlencoded, sino que vamos a enviar el contenido de este formulario a través de un objeto JSON (content-type application/json) enviado por POST. Por supuesto dicho formulario será tratado por un servicio REST que crearemos con WebApi.

Primero lo haremos “a la vieja usanza” es decir usando jQuery para crear el objeto JSON y luego haremos que entre en escena knockout.

A la vieja usanza…

Primero vamos a crear el método de nuestra API que nos devuelva una cerveza según su ID. Así añadimos al controlador ApiBeersController el método GetById:

public Beer GetById(int id)
{
return new Beer {Name = "Imperial Stout #" + id, Abv = 10.0M, Ibu = 120};
}

Ahora toca crear la vista para edición. Dicha vista será retornada por el controlador Beers:

public ActionResult Edit(int? id)
{
ViewBag.BeerId = id ?? 0;
return View();
}

Bien, vayamos a lo que importa, el código de la vista:

@{
ViewBag.Title = "Edit Beer " + ViewBag.BeerId;
}

<script type="text/javascript">
$(document).ready(function () {
var uri = "@Url.RouteUrl("DefaultApi", new {httproute="", controller="ApiBeers", id=ViewBag.BeerId})";
$.getJSON(uri, function(data) {
$("#Name").val(data.Name);
$("#Abv").val(data.Abv);
$("#Ibu").val(data.Ibu);
});

$("#frm").submit(function(evt) {
// Crea el objeto JSON a partir de los datos del formulario
var beer = { };
beer.Name = $("#Name").val();
beer.Abv = $("#Abv").val();
beer.Ibu = $("#Ibu").val();
var jsonBeer = JSON.stringify(beer);
// Enviamos el objeto json
var uriEdit = "@Url.RouteUrl("DefaultApi", new {httproute="", controller="ApiBeers"})";
$.ajax({
url: uriEdit,
dataType: 'json',
contentType: 'application/json',
type: 'post',
data: jsonBeer
}).done(function(data) {
alert("Beer " + data.Id + " has been edited " + data.Status);
});

evt.preventDefault();
});
});

</script>

<h2>Edit a beer! ;-)</h2>

<form method="POST" id="frm">
<label for="Name">Name:</label>
<input type="text" name="Name" id="Name" />
<br />

<label for="Name">Abv:</label>
<input type="text" name="Abv" id="Abv" />
<br />

<label for="Name">Ibu</label>
<input type="text" name="Name" id="Ibu" />
<br />

<input type="submit" value="edit!" />
</form>

Si lo analizamos vemos que hace lo siguiente:

  1. Nada más cargarse la página, llama a la API al método de obtener una cerveza usando $.getJSON. Una vez recibe la cerveza rellena los campos del formulario.
  2. Se suscribe al evento “submit” del formulario. En dicho evento:
    1. Crea un objeto beer y lo rellena con los valores del formulario
    2. Crea la cadena en formato json de dicho objeto (usando JSON.stringify).
    3. Finalmente usa $.ajax para enviar la petición POST hacia la API de editar cervezas pasándole la cerveza a editar.
      1. Una vez la petición ajax se haya realizado muestra un alert con la información devuelta por el servidotr (que a su vez es otro objeto JSON).

Veamos la información que se manda a través de la red:

image 

Vemos tres peticiones:

  1. GET a /beers/edit/10. Es la que termina llamando la acción Edit del BeersController y devuelve la vista.
  2. GET a /api/apibeers/10. Es la que termina llamando a la acción GetById del ApiBeersController
  3. POST a /api/apibeers. Es la que termina llamando a la acción Post del ApiBeersController.

La siguiente imágen muestra la respuesta de la segunda petición, que son los datos de la cerveza en JSON que envía el servidor:

image

Y esta otra imagen muestra los datos enviados en la tercera petición (el $.ajax). Fijaos como el “request payload” es un json y como el content-type es application/json:

image

Y la respuesta recibida del servidor es el objeto JSON que devuelve la acción Post:

image

Bien, ahora vamos a mantener toda la infraestructura de servidor (no vamos a tocar ningún controlador) pero vamos a usar knockout en la vista.

Desplegamos el poder de knockout

En el post anterior vimos como usar data-bind para enlazar una propiedad de nuestro viewmodel a un elemento del DOM. Ahora básicamente vamos a hacer lo mismo. Tendremos un viewmodel con 3 propiedades (Name, Abv, Ibu) y lo enlazaremos a los 3 controles del formulario:

<form method="POST" id="frm">
<label for="Name">Name:</label>
<input type="text" name="Name" id="Name" data-bind="value: Name" />
<br />

<label for="Name">Abv:</label>
<input type="text" name="Abv" id="Abv" data-bind="value: Abv" />
<br />

<label for="Name">Ibu</label>
<input type="text" name="Name" id="Ibu" data-bind="value: Ibu" />
<br />

<input type="submit" value="edit!" />
</form>

Fijaos que ahora usamos data-bind=”value: xxx” para indicar que lo que estamos enlazando es el valor de la propiedad “value” del objeto del DOM al valor de la propiedad xxx de nuestro viewmodel.

Ahora ha llegado el momento de crear nuestro viewmodel. Pero ¡ey! recuerda que esto es un formulario de edición, eso significa que desde los controles podremos editar nuestro viewmodel. Así pues necesitamos un enlace bidireccional. Por supuesto knockout tiene soporte para estos enlaces de forma automática. Es decir, no haría falta que hicieramos nada especial (crear las propiedades en el viewmodel y listos).

Observables

Vamos a introducir un concepto nuevo: los observables. Si vienes del mundo de Silverlight o WPF debes entender que un observable de Knockout viene a ser lo mismo que una propiedad que implemente INotifyPropertyChanged, es decir la UI se enterará de los cambios de dicha propiedad. Insisto en que para que una propiedad del viewmodel sea editable a partir de un control no es necesario que sea un observable. Un observable es solo para que la UI se entere de las modificaciones del viewmodel.

Este es el código para crear nuestro viewmodel a partir de los datos devueltos por la acción GetById de ApiBeersController:

var uri = "@Url.RouteUrl("DefaultApi", new {httproute="", controller="ApiBeers", id=ViewBag.BeerId})";
$.getJSON(uri, function(data) {
vm = {
Name : ko.observable(data.Name),
Ibu : ko.observable(data.Ibu),
Abv : ko.observable(data.Abv)
};
ko.applyBindings(vm);
});

Fijaos en el uso de ko.observable para crear una propiedad observable (el parámetro es el valor inicial). Realmente en este momento Name, Ibu y Abv ya no son variables miembro del viewmodel. Ahora son funciones:

image

Para enviar los datos en el servidor no hay mucha diferencia respecto al caso anterior:

$("#frm").submit(function(evt) {
// Crea el objeto JSON a partir de los datos del formulario
var jsonBeer = JSON.stringify({
Name : vm.Name(),
Ibu : vm.Ibu(),
Abv : vm.Abv()
});
// Enviamos el objeto json
var uriEdit = "@Url.RouteUrl("DefaultApi", new {httproute="", controller="ApiBeers"})";
$.ajax({
url: uriEdit,
dataType: 'json',
contentType: 'application/json',
type: 'post',
data: jsonBeer
}).done(function(data) {
alert("Beer " + data.Id + " has been edited " + data.Status);
});
evt.preventDefault();
});

Creamos la representación en JSON del viewmodel. Fijaos que dado que Name, Ibu y Abv son realmente funciones, no puedo serializar el viewmodel directamente a JSON, en su lugar creo un objeto intermedio, con las propiedades Name, Ibu y Abv invocando a las funciones del viewmodel para obtener el valor actual. Y el resto ya es el mismo código de antes.

Aunque a priori no haya mucha diferencia en cuanto a “la cantidad” de código, hay una diferencia conceptual enorme. En el caso en que usábamos jQuery el código javascript estaba muy atado al DOM. Los datos con los que trabajábamos estaban dispersos (en #Name, en #Abv, …). Ahora en este segundo caso el código javascript es totalmente ignorante del DOM y trabaja tan solo con el viewmodel. Separación de responsabilidades.

¿Y ese submit?

Incluso ahora aunque estemos “aislados” del DOM, seguimos gestionando una parte de nuestro código con eventos vinculados a éste: en concreto estamos suscritos al evento “submit” de un objeto del DOM (el formulario).

¿Nos puede ayudar knockout aquí? Pues claro 🙂

El patrón MVVM también recomienda que el código de interacción esté en el viewmodel. Es decir evitar tener código que no sea exclusivo de presentación en las vistas. En el mundo WPF y Silverlight eso se traduce en evitar el code-behind y enlazar los controles del XAML con comandos que se ejecutan en el viewmodel. Pues knockout nos ofrece algo parecido: ¡Podemos enlazar eventos del DOM a acciones de nuestro viewmodel!

Para empezar le indicamos al formulario que hay un enlace a través del evento submit:

<form method="POST" id="frm" data-bind="submit: editBeer">

Fijaos de nuevo en el uso de data-bind. Ahora enlazamos el evento submit con la función editBeer del viewmodel. Esto es una de las cosas que más me gustan de Knockout: la sintaxis para establecer todos los bindings es muy simple (hemos establecido bindings de texto, bindings bidireccionables y ahora nos enlazamos a una función con la misma sintaxis).

¿Y como es esta función editBeer? Pues bueno es una función de nuestro viewmodel que tiene básicamente el mismo código que teníamos antes dentro del evento “submit” que gestionábamos con jQuery:

vm = {
Name : ko.observable(data.Name),
Ibu : ko.observable(data.Ibu),
Abv : ko.observable(data.Abv),
editBeer : function() {
var jsonBeer = JSON.stringify({
Name : this.Name(),
Ibu : this.Ibu(),
Abv : this.Abv()
});
// Enviamos el objeto json
var uriEdit = "@Url.RouteUrl("DefaultApi", new {httproute="", controller="ApiBeers"})";
$.ajax({
url: uriEdit,
dataType: 'json',
contentType: 'application/json',
type: 'post',
data: jsonBeer
}).done(function(data) {
alert("Beer " + data.Id + " has been edited " + data.Status);
});
}
};

¡Fantástico! Ahora ya NO tenemos nada fuera de nuestro viewmodel. Hemos establecido una separación total entre la vista (el HTML) y el viewmodel (el objeto vm) que mantiene no solo los valores que tiene la vista, si no también gestiona los comandos que la vista puede realizar.

Puede seguir pareciendo que en cuanto a “cantidad” de código javascript no hemos mejorado mucho respecto a la versión inicial que no usaba knockout, pero es indudable que a nivel de claridad y mantenibilidad nuestro código es ahora mucho, mucho, mucho mejor.

Espero que os haya resultado interesante.

¡En el siguiente post seguiremos explorando las maravillas de knockout!

PD: He dejado el código (VS2012 RC) del proyecto (KoDemo1) que se corresponde a este post. En la carpeta Views/Beers vereis que hay una Edit.cshtml y otra Edit-no-ko.cshtml. La segunda contiene el código sin usar knockout.

Y el combate se decidió por KO (i)

Hace algunas semanas salió un post de Shaun Walker titulado “Microsoft Declares the future of ASP.NET is Web API”. La verdad es que el post es interesante. Yo no sé cuales serán las intenciones de Microsoft (creo que ni ellos las saben realmente) pero lo que si es cierto es que las aplicaciones web están realmente cambiando a un modelo donde cada vez se procesa más en cliente y menos en servidor. Es un modelo que deja totalmente obsoleto no solo a Webforms si no incluso a ASP.NET MVC.

En este modelo las páginas o vistas de una aplicación web, ya no son servidas desde el servidor. En todo caso, se sirve un “bootstrapper” inicial, que contiene el código javascript que realiza la petición inicial y luego todo es modificar el DOM en cliente a partir de datos recibidos por servicios REST, usando javascript. Por lo tanto dejamos de servir contenido de presentación (HTML generado dinámicamente) desde el servidor, para servir tan solo datos. Y la conversión de datos a algo visible (DOM) se realiza en el propio cliente, usando javascript.

A nivel tecnológico, salvando quizá cuestiones de rendimiento (que no son en absoluto despreciables, ahí está la experiencia de Twitter) estamos ya bastante preparados para dar este salto. Y aquí entra lo que os quería comentar en el post de hoy. Vamos a iniciar una serie, de longitud desconocida, hablando de Knockout, una librería javascript que resumiendo lo que hace es implementar el patrón MVVM en javascript.

Como siempre no soy el primero en hablar de Knockout en geeks. Concretamente el Maestro se me ha avanzado con un post (http://geeks.ms/blogs/jmaguilar/archive/2012/05/07/knockout-i-pongamos-orden-en-el-lado-cliente.aspx).

También está Marc Rubiño que tiene una presentación en slideshare: http://www.slideshare.net/webcatbcn/javascript-con-mvvm-knockout-por-marcrubino 

Pero por encima de todos Sergio Marrero que tiene una serie impresionante sobre Knockout en su blog: http://smarrerof.blogspot.com.es/search/label/knockout

Pero bueno… Yo intentaré aportar mi grano de arena y intentaré acercaros un poco Knockout para que los que no lo conozcáis os echeis las manos a la cabeza y os preguntéis como narices habeis podido hacer aplicaciones web todo este tiempo 🙂

¡Espero que esta sea una larga serie de posts, porque realmente hay mucho que contar!

Introducción a Knockout

Knockout es una librería javascript que básicamente nos da soporte para el patrón MVVM. Este patrón, bien conocido para los que desarrollan en WPF, Silverlight o WinRT, es en realidad un MVP Supervising Controller con dos peculiaridades:

  1. Se crea un modelo específicamente para presentación. Es decir, no se usa el “Modelo” real para tareas de presentación, si no que se crea otro modelo específicamente para ello. En muchos casos este otro modelo está adaptado y optimizado para cada vista. En MVVM conocemos a este otro modelo con el nombre de ViewModel.
  2. Se promueve que la comunicación entre la vista y su viewmodel sea a través de bindings (bidireccionales).

Lo que Knockout nos permite hacer de forma muy rápida es, realizar estos bindings entre elementos del DOM y nuestro viewmodel así como crear elementos del DOM a partir del propio viewmodel (lo que antes hacíamos con jquery_tmpl p.ej.)

Hello Knockout

Vamos a empezar con el primer proyecto usando Knockout. En este caso vamos a montar una tabla que muestre información de cervezas. Para ello, nos creamos un nuevo proyecto de tipo WebApi, donde vamos a añadir un controlador:

   1: public class ApiBeersController : ApiController

   2: {

   3:     public IEnumerable<Beer> GetAll()

   4:     {

   5:         yield return new Beer { Name = "Estrella Damm", Abv = 5.4M, Ibu = 21 };

   6:         yield return new Beer { Name = "Heineken", Abv = 5.0M, Ibu = 10 };

   7:     }

   8: }

La clase Beer simplemente contiene las propiedades Name, Abv e Ibu.

Si ahora abrimos una URL y navegamos a /api/apibeers deberíamos recibir un json:

[{"Name":"Estrella Damm","Abv":5.4,"Ibu":21},{"Name":"Heineken","Abv":5.0,"Ibu":10}]

Nota: Si recibes un XML en lugar de un JSON eso es debido a la cabecera Accept que envía el navegador. Una solución para ello es indicarle a WebApi que nunca devuelva resultados en XML, lo que se consigue colocando la siguiente línea en el Application_Start: GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);

 

Ahora vamos a incluir a Knockout en nuestra página master.

Para ello damos de alta un nuevo bundle para incluir Knockout (en el método RegisterBundles de la clase BundleConfig):

   1: bundles.Add(new ScriptBundle("~/bundles/ko").Include(

   2:                         "~/Scripts/knockout-2*"));

Ok, ahora vamos a crear un controlador “normal” que nos devuelva la vista que debe mostrar las cervezas:

   1: <h2>Index de cervezas</h2>

   2:  

   3: <script type="text/javascript">

   4:     $(document).ready(function () {

   5:         var url="@Url.RouteUrl("DefaultApi", new {httproute="", controller="ApiBeers"})";

   6:         $.getJSON(url, function(data) {

   7:             ko.applyBindings({beers: data});

   8:         });

   9:     });

  10: </script>

  11:  

  12:  

  13: <div id="beers">

  14:     <table>

  15:         <tbody data-bind="foreach:beers">

  16:             <tr>

  17:                 <td data-bind="text: Name" />

  18:                 <td data-bind="text: Abv" />

  19:                 <td data-bind="text: Ibu" />

  20:             </tr>

  21:         </tbody>

  22:     </table>

  23: </div>

Analicemos el código:

  1. Al cargarse la página usamos $.getJSON para obtener los datos de las cervezas
  2. Una vez hemos obtenido el objeto JSON con las cervezas (recordad que era un IEnumerable<Beer> en C# y un array puro en javascript) creamos un objeto con una propiedad llamada “beers” cuyo contenido es precisamente este array de cervezas.
  3. Llamamos al método applyBindings, para Knockout haga su magia. A applyBindings se le pasa el viewmodel a usar.

¿Y como hace el enlace Knockout? Pues, y ahí radica su gracia, usa un atributo propio llamado data-bind. Recordad que HTML5 nos permite definir nuestros propios atributos siempre y cuando empiecen por data-. Knockout buscará los elementos que estén marcados con este atributo data-bind y realizará distintas tareas según el valor de dicho atributo y del viewmodel definido. En este primer ejemplo vemos dos usos de data-bind:

  • foreach: propiedad –> Itera sobre todos los elementos de la propiedad especificada. En nuestro caso usamos foreach:beers, ya que beers es el nombre de la propiedad de nuestro viewmodel que contiene el array de cervezas. Por cada elemento de la propiedad beers, knockout repetirá todos los elementos que estén dentro del elemento que contiene el foreach. En nuestro caso creará un <tr> por cada elemento.
  • text: propiedad –> Muestra el texto de la propiedad especificada. Como estamos dentro de un foreach muestra la propiedad especificada del elemento que se está “renderizando”.

El resultado de crear esta vista es, tal y como seguro que esperabais:

image

Bueno… ¿no está nada mal por un par de líneas, no? ¿A que ya os está picando la curiosidad?

En los próximos posts iremos ampliando y viendo muchas otras cosas que Knockout nos ofrece! 😀

Bundles en ASP.NET MVC4

¡Buenas! Este va a ser un post cortito, sobre los Bundles en ASP.NET MVC. Los bundles es el mecanismo que tiene ASP.NET MVC para incluir varios ficheros (de script o css) que están relacionados entre ellos.

Si os creáis un proyecto de ASP.NET MVC4 nuevo (sin que sea la plantilla Empy, claro) veréis el siguiente código en la página de Layout:

   1: <!DOCTYPE html>

   2: <html>

   3: <head>

   4:     <meta charset="utf-8" />

   5:     <meta name="viewport" content="width=device-width" />

   6:     <title>@ViewBag.Title</title>

   7:     @Styles.Render("~/Content/themes/base/css", "~/Content/css")

   8:     @Scripts.Render("~/bundles/modernizr")

   9: </head>

  10: <body>

  11:     @RenderBody()

  12:  

  13:     @Scripts.Render("~/bundles/jquery")

  14:     @RenderSection("scripts", required: false)

  15: </body>

  16: </html>

En lugar de incluir directamente las etiquetas <link> (para las CSS) o <script> para referenciar a archivos js externos, lo que tenemos son llamadas a Styles.Render y Scripts.Render.

El parámetro que se pasa a esos métodos Render es el nombre del bundle a renderizar. ¿Y donde están definidos los bundles? Pues en el archivo App_Start/BundleConfig tenéis el siguiente método:

   1: public static void RegisterBundles(BundleCollection bundles)

   2: {

   3:     bundles.Add(new ScriptBundle("~/bundles/jquery").Include(

   4:                 "~/Scripts/jquery-1.*"));

   5:  

   6:     bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include(

   7:                 "~/Scripts/jquery-ui*"));

   8:  

   9:     bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(

  10:                 "~/Scripts/jquery.unobtrusive*",

  11:                 "~/Scripts/jquery.validate*"));

  12:  

  13:     bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(

  14:                 "~/Scripts/modernizr-*"));

  15:  

  16:     bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/site.css"));

  17:  

  18:     bundles.Add(new StyleBundle("~/Content/themes/base/css").Include(

  19:                 "~/Content/themes/base/jquery.ui.core.css",

  20:                 "~/Content/themes/base/jquery.ui.resizable.css",

  21:                 "~/Content/themes/base/jquery.ui.selectable.css",

  22:                 "~/Content/themes/base/jquery.ui.accordion.css",

  23:                 "~/Content/themes/base/jquery.ui.autocomplete.css",

  24:                 "~/Content/themes/base/jquery.ui.button.css",

  25:                 "~/Content/themes/base/jquery.ui.dialog.css",

  26:                 "~/Content/themes/base/jquery.ui.slider.css",

  27:                 "~/Content/themes/base/jquery.ui.tabs.css",

  28:                 "~/Content/themes/base/jquery.ui.datepicker.css",

  29:                 "~/Content/themes/base/jquery.ui.progressbar.css",

  30:                 "~/Content/themes/base/jquery.ui.theme.css"));

  31: }

Dicho método, que es llamado desde el Application_Start, es donde se asocia cada bundle con un conjunto de archivos (fijaos que se puede usar * para indicar un conjunto de archivos).

Así vemos que el bundle ~/bundles/modernizr se compone de los archivos que estén en ~/scripts y cuyo nombre empiece por mondernizr-

Esto en tiempo de ejecución no hace nada especial, la verdad. Si abrimos cualquier vista y le damos a “ver código fuente” lo que vemos es simplemente el conjunto de tags <link> o <script>:

   1: <link href="/Content/themes/base/jquery.ui.core.css" rel="stylesheet" type="text/css" />

   2: <link href="/Content/themes/base/jquery.ui.resizable.css" rel="stylesheet" type="text/css" />

   3: <link href="/Content/themes/base/jquery.ui.selectable.css" rel="stylesheet" type="text/css" />

   4: <link href="/Content/themes/base/jquery.ui.accordion.css" rel="stylesheet" type="text/css" />

   5: <link href="/Content/themes/base/jquery.ui.autocomplete.css" rel="stylesheet" type="text/css" />

   6: <link href="/Content/themes/base/jquery.ui.button.css" rel="stylesheet" type="text/css" />

   7: <link href="/Content/themes/base/jquery.ui.dialog.css" rel="stylesheet" type="text/css" />

   8: <link href="/Content/themes/base/jquery.ui.slider.css" rel="stylesheet" type="text/css" />

   9: <link href="/Content/themes/base/jquery.ui.tabs.css" rel="stylesheet" type="text/css" />

  10: <link href="/Content/themes/base/jquery.ui.datepicker.css" rel="stylesheet" type="text/css" />

  11: <link href="/Content/themes/base/jquery.ui.progressbar.css" rel="stylesheet" type="text/css" />

  12: <link href="/Content/themes/base/jquery.ui.theme.css" rel="stylesheet" type="text/css" />

  13: <link href="/Content/site.css" rel="stylesheet" type="text/css" />

  14:  

  15: <script src="/Scripts/modernizr-2.0.6-development-only.js" type="text/javascript"></script>

Pero lo bueno de usar los bundles no es tan solo poder agrupar lógicamente nuestros archivos javascript o css. Lo bueno es que estamos a una sola propiedad de distancia de poder agruparlos y minificarlos (pésima palabreja lo sé, pero soy incapaz de encontrar una traducción para minification).

Así, si en el Application_Start añadimos la línea:

   1: BundleTable.EnableOptimizations = true;

Si ahora ejecutamos y miramos el código fuente veremos lo siguiente:

   1: <link href="/Content/themes/base/css?v=UM624qf1uFt8dYtiIV9PCmYhsyeewBIwY4Ob0i8OdW81" rel="stylesheet" type="text/css" /> 

   2: <link href="/Content/css?v=ZAyqul2u_47TBTYq93se5dXoujE0Bqc44t3H-kap5rg1" rel="stylesheet" type="text/css" /> 

   3: <script src="/bundles/modernizr?v=EuTZa4MRY0ZqCYpBXj_MhJfFJU2QBDf0xGrV_p1fHME1" type="text/javascript"></script>

Podemos ver como tenemos tan solo dos tags <link> y uno <script> en lugar de todos los tags de antes. Ahora cada Bundle ha generado un solo tag. Eso, ya de por sí es bueno, ya que se reduce el número de peticiones http (una de las cosas que más tardan a la hora de cargar una página web es ir abriendo y cerrando conexiones http).

Pero no solo eso, sino que además ahora el contenido está “minificado”, es decir se han eliminado todos los espacios y saltos de línea redundantes y se renombran todas las variables y nombres de función posibles para que tengan una longitud menor, de forma que el tamaño final sea menor, lo que a su vez redunda en un mejor rendimiento.

Y todo ello tan solo habilitando la EnableOptimizations.

Un par de curiosidades…

Hoy en día muchas librerías js incluyen dos .js: uno sin normal y el otro minificado. Usualmente (por una de esas convenciones que terminan siendo estándares de facto) el archivo minificado tiene el sufijo “min”. Pongamos a jQuery como ejemplo. Si te lo descargas verás dos archivos como p.ej.

  1. jquery-1.6.2.js
  2. jquery-1.6.2.min.js

El primero es el archivo .js normal (para usar en debug p.ej.) y el otro es el archivo minificado. Funcionalmente ambos son equivalentes pero el primero ocupa unas 230K y el segundo unas 91.

Pues bien, imaginad que tenemos un bundle para jQuery, definido de la siguiente forma:

   1: bundles.Add(new ScriptBundle("~/bundles/jquery")

   2: .Include("~/Scripts/jquery-1.6.2*"));

Fijaos que es un bundle que incluiría cualquier archivo js cuyo nombre empezase por jquery-1.6.2. Y en nuestro directorio ~/Scripts tenemos dos archivos que empiezan por este nombre, jquery-1.6.2.js y jquery-1.6.2.min.js. Pues bien el bundle es lo suficientemente inteligente como:

  • Generar tan solo el tag <script> para el archivo jquery-1.6.2.js en el caso de que las optimizaciones estén deshabilitadas (y no generar el tag para el archivo jquery-1.6.2.min.js).
  • Retornar el contenido del archivo jquery-1.6.2.min.js en el caso de que las optimizaciones estén habilitadas. Aunque ASP.NET igualmente minifica el propio archivo (aunque ya esté minificado). Es decir coje el archivo jquery-1.6.2.min.js, lo minifica y devuelve el contenido. Debo reconocer que me ha sorprendido que minifique de nuevo el contenido, pero es es lo que hace.

De hecho en el propio Solution Explorer de VS2012 ya se aprecia que VS “sabe” que ambos archivos están relacionados:

image

Espero que este post os haya resultado de interés! 😉

Saludos!

[OT] El peor móvil de la história

Este es un post para desahogarme… 🙂

¿Cual es para vosotros el peor móvil de la historia? O al menos, ¿el peor móvil que nunca hayáis tenido? El mío, lo tengo clarísimo: El LG Optimus 2X del que soy un desgraciado poseedor ahora mismo.

La verdad es dificil hacerlo tan mal como lo ha hecho LG. Parece imposible tanta incompetencia junta a todos los niveles.

Este móvil era el estandarte de LG cuando salió a principios del 2011. Fue el primer móvil en tener doble núcleo, y así lo anunciaron. Vigilad siempre si cuando os anuncian un móvil lo que más os recalcan es el hardware. Vigiladlo muy, pero que muy bien. El Optimus 2X era (y sigue siendo) una bestia parda. Un hardware excelente para su época. Evidentemente ahora palidece un poco por culpa de esa estúpida carrera en ver quien monta el pepinaco más grande en que se ha convertido el desarrollo de móviles Android, pero sigue siendo un excelente hardware hoy en día.

El problema es que en LG parecen no saber (y lo que es peor, ni querer enterarse) de que un teléfono no es un pedazo hierro. Yo cuando compro un móvil lo que quiero es poder usar aplicaciones, jugar, escuchar música. navegar por internet y de vez en cuando hasta llamar y todo. El problema es que ninguna de esas cosas es factible con el Optimus 2X. ¿La razón? La peor ROM de Android que pueda ser compilada nunca jamás. Un listado ni mucho menos completo de “maravillas” de las que es capaz el Optimus 2X incuye lo siguiente:

  1. Colgarse cuando te llaman. Quedarse el móvil tan frito, que debes reiniciarlo quitando la batería.
  2. Perder la cobertura y no recuperarla hasta que se reinicia el móvil. A veces ocurre con los datos. A veces se debe reiniciar varias veces.
  3. La que ocurre más a menudo: le coge un jamacuco y entonces todo va muy lento (pero muy lento eh? pulsar un botón puede llevar MINUTOS). El problema es que este jamacuco puede durar una hora… o más. Cuando eso ocurre la única solución es quitar la batería, ya que está tan frito que ni el botón de parar responde. Lo más triste es que a veces al cabo de 10 minutos de volver a arrancarlo está igual.
  4. Continuamente salen mensajes de que la aplicación XXX (la que queráis) no responde. Por supuesto el móvil está tan frito cuando eso ocurre que darle al botón de “cerrar aplicación” es una tarea titánica, que puede llevar cinco minutos o más.
  5. Escuchar música a veces es como escuchar canal+, de lo entrecortada que se oye.
  6. No se conecta con el Exchange de mi curro… Contando con que es el móvil profesional eso lo invalida bastante. Tengo que usar un programa externo, lo que implica que me puedo olvidar de sincronizar calendarios o contactos. Eso puede ser culpa de la configuración del exchange, no lo niego pero… el resto de móviles Android no tienen ningún problema.ç

Lo más triste es que a veces te “regala” con algunos minutos de funcionamiento perfecto, donde se vislumbra el potencial que el móvil tiene: todo va fluido, perfecto, rápido, suave… hasta que al cabo de unos minutos aparece su verdadera naturaleza y es un infierno de nuevo.

Eso convierte al Optimus 2X en un móvil que tan solo sirve para estamparlo contra la pared (más de una vez he estado a punto, lo juro). Y la culpa es LA PUTA MIERDA DE SOFTWARE DE LG. Yo desde mi humilde tribuna le pido a LG que si quiere modificar Android, contrate a desarrolladores que sepan un poco. Y que prueben su producto. Porque sacar un móvil con el software que trae el Optimus 2X debería estar penado con penas de cárcel… Y cada actualización que sale (tampoco muchas no os penséis) no sirve para nada. Hace poco sacaron una que me ha dejado el móvil igual de mal que antes.

Y es que si encima hablamos de las actualizaciones ya es para llorar. Porque lo bueno es que cuando me pasé al LG Optmus 2X, un servidor ya tenía Gingerbread instalado en su Galaxy S. Y el Optimus 2X salió con Froyo. Pero, ¡no pasa nada! Desde LG juraron y perjuraron que en breve salía la actualización a Gingerbread…

… y salió más de medio año después. De hecho, salió la actualización a Gingerbread poco antes de que Google liberase ICS. Evidentemente preguntarle a LG para cuando la actualización era totalmente inútil. Su respuesta era siempre la misma “no lo sabemos”, “ya daremos fechas”, “no falta mucho”, “en breve” o cualquier otra excusa… Para una muestra reciente: https://twitter.com/LG_ES/status/222247495063973888 o https://twitter.com/LG_ES/status/222211821753483264 o tantas otras que podéis encontrar navegando por su timeline.

¿La solución? Lo que voy a hacer justo ahora: meter una ROM no oficial y listos. Peor es imposible que me vaya el móvil. Pero eso no debería ser así. Por mi parte no pienso tener nunca jamás un móvil LG… y nada suyo que lleve software ya de paso.

Ale… ya me he desfogado.

ASP.NET MVC: Encriptar RouteValues

Muy buenas! El otro día publicaba en mi blog una solución para encriptar la querystring en ASP.NET MVC. A raiz de este post, me preguntaron si era posible hacer lo mismo pero en el caso de que tengamos URL amigables y como se podría hacer.

La respuesta es que sí, que se puede hacer y que a diferencia del caso de la querystring tenemos dos opciones.

Opción 1 – Value Provider

En este caso el planteamiento es el mismo que en el post anterior, debemos crear un value provider que recoja los datos y los desencripte.

Así, empezamos por crear la factoría de nuestro nuevo value provider:

public class RouteValuesEncriptedValueProviderFactory : ValueProviderFactory
{
    public override IValueProvider GetValueProvider(ControllerContext controllerContext)
    {
        return new RouteValuesEncriptedValueProvider(controllerContext);
    }
}

Y el código de nuestro value provider, muy parecido al del post anterior. La diferencia está en que en lugar de mirar en la request los valores de la querystring, miramos en RouteData que es donde están guardados todos los valores de ruta. Los valores de ruta se extraen a partir del PathInfo, es decir la parte de la URL que va después del host hasta el inicio de la querystring. El código es muy sencillo:

class RouteValuesEncriptedValueProvider : DictionaryValueProvider<string>
{

    public RouteValuesEncriptedValueProvider(System.Web.Mvc.ControllerContext controllerContext)
        : base(GetRouteValueDictionary(controllerContext), Thread.CurrentThread.CurrentCulture)
    {
    }

    private static IDictionary<string, string> GetRouteValueDictionary(ControllerContext controllerContext)
    {
        var dict = new Dictionary<string, string>();
        foreach (var key in controllerContext.RouteData.Values.Keys.Where(x => x.First() == '_'))
        {
            var value = controllerContext.RouteData.GetRequiredString(key);
            var decripted = Crypto.DecryptValue(value);
            dict.Add(key.Substring(1), decripted);
        }
        return dict;
    }
}

El funcionamiento es análogo al del value provider del post anterior y actúa sobre los route values cuyo nombre empieze por un subrayado (aunque por supuesto esto va a gustos del consumidor). Dado que el nombre de los route values se establece en la tabla de rutas, debemos cambiar la definición para que allí los nombres empiecen por un subrayado:

routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{_id}",
    defaults: new { controller = "Home", action = "Index", _id = UrlParameter.Optional }
);

Al igual que en el value provider del post anterior, al añadir el valor desencriptado de un route value, lo hace eliminando el subrayado inicial, por lo que en nuestros controladores podemos declarar el parámetro como “id” y recibiremos el valor desencriptado (si lo declarásemos como _id lo recibiríamos encriptado).

Pero, en este caso especial tenemos otra opción distinta para desencriptar los valores de ruta…

Opción 2: Un custom route handler

Hemos visto como este value provider nuevo no accede a la request, ni mira la URL. En su lugar accede a los valores de ruta (controllerContext.RouteData). Pero… ¿quien rellena esto? Pues el route handler.

Cada vez que una ruta es seleccionada, se ejecuta su route handler, que es el encargado de rellenar los valores de ruta. ASP.NET MVC viene con un route handler por defecto, pero nos podemos crear el nuestro. En este caso, nada nos impide crear un route handler que desencripte los valores antes de añadirlos:

public class UnencriptedRouteHandler : MvcRouteHandler
{
    protected override IHttpHandler GetHttpHandler(System.Web.Routing.RequestContext requestContext)
    {
        foreach (var rd in requestContext.RouteData.Values.Where(x=>x.Key.First()== '_').ToList())
        {
            var value = rd.Value.ToString();
            var decrypted = !string.IsNullOrEmpty(value) ?
                Crypto.DecryptValue(value) :
                string.Empty;
            requestContext.RouteData.Values.Remove(rd.Key);
            requestContext.RouteData.Values.Add(rd.Key.Substring(1), decrypted);
        }


        return base.GetHttpHandler(requestContext);
    }
}

Si miramos el código, vemos como accedemos a los route values (que en este punto ya están relleanados por el route handler por defecto del cual derivamos) y buscamos aquellos que empiecen por un subrayado y en su lugar los colocamos de nuevo pero desencriptados (y sin subrayar).

Ahora debemos indicarle a ASP.NET MVC que use este route handler nuestro, y eso se hace en la definición de la tabla de rutas:

routes.Add(
    "Default",
    new Route("{controller}/{action}/{_id}",
                new RouteValueDictionary(new { controller = "Home", action = "Index", _id = UrlParameter.Optional }),
                new UnencriptedRouteHandler())
);

En lugar de usar MapRoute, usamos el método Add, el cual espera un objeto Route al cual de lo podemos especificar cual será su route handler. Además fijaos en que hemos declarado _id en lugar de id, eso es porque nuestro route handler actuará tan solo sobre los route values que empiecen por el subrayado.

Con esto ya hemos terminado. Ahora, si generamos URLs que tengan el _id encriptado:

@Html.ActionLink("Test", "Test", 
new {_id=MvcApplication4.Crypto.EncryptValue("bar value")}, null)

Podemos declarar en el controlador la acción que espere un parámetro llamado id que tendrá el valor ya desencriptado. A diferencia de la opción anterior, ahora si usamos _id como parámetro recibiremos null. El valor encriptado se ha perdido y no existe a todos los efectos.

Usando esta segunda opción no hay necesidad alguna de crear un value provider propio.

¿Y las diferencias?

Las diferencias entre las dos opciones son básicamente el momento en que se produce la desencriptación:

  1. En la primera opción (value provider), la desencriptación se produce tan solo en el momento de enlazar los parámetros al controlador. Y la desencriptación es tan solo visible para el model binder. Así p.ej. si hay filtros que actuen sobre los valores de ruta, estos verán los valores encriptados.
  2. En la segunda opción, justo al inicio de procesarse la request, antes incluso de decidirse que controlador y que acción deben ser invocados, los valores de ruta son desencriptados. El valor encriptado desaparece y todo el mundo (y no sólo el model binder) tiene acceso a los valores desencriptados.

Si me preguntáis en mi caso que opción escogería… probablemente la segunda 🙂

Un saludo!

PD: En el caso de la querystring no existe la posibilidad de esta segunda opción. Los route handlers actúan tan solo sobre los valores de ruta, no sobre la querystring, por lo que el value provider es la única opción.

ASP.NET MVC: Encriptar la query string

Buenas! Este post surge debido a esta pregunta del foro de ASP.NET MVC. El usuario se pregunta si existe en el framework una manera built-in de encriptar la query string. Y la realidad es que no, no la hay, pero añadir una es muy sencillo y me da una excusa perfecta para poner un buen ejemplo del poder de los value providers.

ASP.NET MVC está construído de una forma bastante flexible, pero en el pipeline de una petición hay más o menos 4 pasos:

  1. Procesar la url para generar unos valores de ruta
  2. En base a dichos valores seleccionar una acción de un controlador
  3. Explorar la petición http en busca de parámetros (que pueden estar en la querystring, en formdata, etc)
  4. A partir de esos parámetros rellenar los parámetros de la acción del controlador

De todos esos pasos, el tercero es responsabilidad de los value providers: ellos inspeccionan la petición http en busca de parámetros y los dejan en “un sitio común”. Luego, en el paso cuatro, el model binder recoge los parámetros de este sitio común y a partir de ellos instancia los parámetros de la acción del controlador.

Ahora supongamos que nuestra querystring tiene algún parámetro encriptado. Para este post he supuesto que algunos parámetros de la querystring pueden ir encriptados, mientras que otros no (no se encripta toda la querystring, aunque la técnica seria la misma). Para el ejemplo supongo que los parámetros encriptados tienen un nombre que empieza por un subrayado (y no, el nombre en si mismo no se encripta).

Así, p.ej. voy a tener una querystring del tipo:

/accion/controlador?_foo=aIGf0UYsNARiBaGZr3blRg%3D%3D

Donde el valor del parámetro _foo está encriptado.

Ahora bien, a mi me gustaría evitar tener que colocar el código para desencriptar dichos valores en cada acción:

public ActionResult Test(string _foo)
{
    // Codigo a evitar:
    var realfoo = Crypto.DecryptValue(_foo);
    // ...
}

Aquí es donde entran los value providers. ASP.NET MVC trae de serie varios value providers que “inspeccionan” una petición http y miran los sitios más habituales donde se pueden encontrar datos:

image

Los value providers se registran en el sistema a través de una factoría, y esta captura muestra las factorías de value providers registradas por defecto en ASP.NET MVC4 RC. Cada factoría devuelve un tipo de value provider, es decir un value provider que mira en una parte de la petición http y por el nombre se puede más o menos deducir:

  1. ChildActionValueProviderFactory: Se usa cuando se invocan acciones hijas (a través de Html.Action o de Html.RenderAction). Es más de infraestructura de ASP.NET MVC que otra cosa.
  2. FormValueProviderFactory: Devuelve un value provider encargado de inspeccionar los parámetros de formdata (básicamente formularios enviados via POST).
  3. JsonValueProviderFactory: Introducido en MVC3 devuelve un value provider que inspecciona la petición http, buscando datos en el cuerpo de ésta que estén en formato json. Es el responsable de que en MVC3 puedas hacer un POST enviando datos en formato json y que el model binder los entienda.
  4. RouteDataValueProviderFactory: Devuelve un value provider que inspecciona los route values, o sea los parámetros establecidos a través de la tabla de rutas.
  5. QueryStringValueProviderFactory: Devuelve un value provider que inspecciona la querystring.
  6. HttpFileCollectionValueProviderFactory: Devuelve un value provider que inspecciona los datos de los ficheros subidos (usando <input type=”file”>).

Por supueso nosotros podemos crearnos nuestras propias factorías de value providers, y eso es justamente lo que haremos. En nuestro caso vamos a hacer un value provider que:

  1. Inspeccione la query string
  2. Seleccione aquellos parámetros que empiezan por un subrayado.
  3. Desencripte cada uno de esos parámetros y guarde en “este sitio común” dicho parámetro, pero con el valor desencriptado. Además el nombre del parámetro en el “sitio común” lo modificaremos quitándole el subrayado.

Veamos primero el código de la factoría:

public class QSEncriptedValueProviderFactory : ValueProviderFactory
{
    public override IValueProvider GetValueProvider(ControllerContext controllerContext)
    {
        return new QSEncriptedValueProvider(controllerContext);
    }
}

Simple no? 🙂 Nos limitamos a devolver una instancia de nuestro value provider propio. Veamos su código:

 

public class QSEncriptedValueProvider : DictionaryValueProvider<string>
{
    public QSEncriptedValueProvider(ControllerContext controllerContext) : base(GetQSDictionary(controllerContext), Thread.CurrentThread.CurrentCulture)
    {
    }

    private static IDictionary<string, string> GetQSDictionary(ControllerContext controllerContext)
    {
        var dict = new Dictionary<string, string>();
        var req = controllerContext.HttpContext.Request;
        foreach (var key in req.QueryString.AllKeys.Where(x => x.First() == '_'))
        {
            var value = req.QueryString[key];
            dict.Add(key.Substring(1),Crypto.DecryptValue(value));
        }

        return dict;
    }

}

Derivamos de la clase base DictionaryValueProvider<string>. Esto lo que significa es que este value providers, tan solo dejará strings en “este sitio común”. Bueno, esto es lógico ya que en la querystring tan solo hay strings. Pero p.ej. otros value providers podrían dejar objetos enteros en este “sitio común” (como el que inspecciona la petición buscando datos en json).

Al derivar de esta clase obtenemos la facilidad de trabajar con un diccionario de cadenas (en nuestro caso de <string,string> ya que las claves siempre son cadenas). Básicamente, lo que dejemos en este diccionario es lo que se colocará en este “sitio común”. Y eso es lo que hace el método GetQSDictionary.

Mirando el código vemos que selecciona todas las claves de la querystring que empiecen por un subrayado, y por cada uno de ellas:

  • Desencripta su contenido
  • Guarda el valor desencriptado con la misma clave, pero quitando el subrayado del principio.

¡Ya estamos listos! Ahora tan solo debemos registrar la factoría de value providers. Para ello en el Application_Start podemos colocar la línea:

ValueProviderFactories.Factories.Add(new QSEncriptedValueProviderFactory());

¡Ya podemos probarlo!

Para ello me creo una vista con un link que tenga un parámetro encriptado:

@Html.ActionLink("Test", "Test", 
new {_foo=MvcApplication4.Crypto.EncryptValue("bar value")}, null)

El código fuente generado es el siguiente:

<a href="/Home/Test?_foo=aIGf0UYsNARiBaGZr3blRg%3D%3D">Test</a>

Podemos ver como el valor “bar value” ha sido encriptado. Y ahora viene lo bueno: en nuestro controlador declaramos la acción Test que recibe una cadena, y eso es lo que obtenemos:

image

En el controlador obtenemos ya el valor desencriptado! Fijaos además como el parámetro de la acción se llama foo (en lugar de _foo). Eso es porque en el value provider hemos eliminado el subrayado inicial. Por supuesto dado que también existe el value provider que procesa la querystring de forma “normal”, si en la acción declaramos un parámetro _foo obtendremos el valor encriptado.

Esta es una breve demostración del poder de los value providers. Por supuesto, deberíamos crearnos un conjunto de helpers para generar URLs encriptadas de forma más fácil, pero eso ya daría para otro post.

Un saludo!

PD: No he puesto el código de encriptar/desencriptar adrede porque hay muchas maneras de hacerlo. Yo en mi caso he usado uno que he encontrado en http://www.joshrharrison.com/archive/2009/01/28/c-encryption.aspx (aunque ello no es relevante para lo que cuenta este post).

Microsoft MVP 2012

¡Muy buenas!

Hoy al mirar el correo me he llevado una más que grata sorpresa: he sido reconocido como MVP en el área de ASP.NET / IIS.

Es para mi un honor pertenecer a este grupo, y además en la categoría de ASP.NET junto con monstruos de la talla de José M. Aguilar o Marc Rubiño sin ir más lejos!

Y ya que estoy en mi tarima particular, me gustaría aprovechar para dar las gracias a todos los que han hecho que entrase en eso que se conoce como “la comunidad”. Soys muchos, pero quiero dedicar unas “gracias especiales” al gran Bandolero, cuando nos presentaron un buen día, hace ya algunos añitos, allí en Igualada y me lió con todo lo de CatDotNet. ¡José Miguel, eres todo un grande!

Y no quiero poner ningún nombre más, aunque me vienen varios, muchos, a la cabeza (de catalunya, de españa entera, de andorra incluso), pero me dejaría alguno y no sería justo. A todos ellos, muchas gracias. Y muchas gracias también a todos aquellos que solo conozco virtualmente, a través de twitter o facebook. Siempre queda la esperanza (más o menos lejana según el caso) de conocernos en persona algún día!

Y por supuesto: ¡felicidades a todos los MVP de hoy, nuevos o renovados!

Un abrazo a todos!