Angularjs

El motivo de esta entrada no es otro que informaros que mi gran amigo y compañero @XaviPaper junto con un servidor, nos hemos marcado como meta escribir en desarrolloweb una serie de post de Angularjs, con no otro objetivo que transmitir nuestras experiencias con esta librería, derivado de su utilización en varios proyectos realizados a lo largo de 2013.

Como no soy de escribir esta serie de entradas, lo que voy a hacer es poneros a todos un aperitivo de cuales son nuestros objetivos y por tanto empieza la fiesta……

La realidad que el titulo de este post no debería de ser este sino más bien y fiel a mi estilo este otro.

Server-sent DOM events o más bien con la iglesia hemos topado.

Y porque?, pues sencillo simplemente hay que mirar el siguiente cuadro para darnos cuenta a que me estoy refiriendo.

image 

Referencia original: caniuse.

Aunque está claro que este tipo de tecnologías actualmente las podemos reemplazar con otras más modernas como websocket o con otras más antiguas como Long polling, lo que me gusta de esta frente a websocket no es otra que utilizamos el protocolo http sin más.

Como referencia he utilizado una serie de artículos y sobre todo el confirmar que existe algún que otro pollyfill.

Stream Updates with Server-Sent Events

Introduction to Server-Sent Events

EventSource polyfill

W3C Candidate Recommendation 11 December 2012

Y como con artículos de 2010 y una recomendación de diciembre de 2012 sigue IE sin implementarlo.

Será por algo?. La verdad es que es algo inexplicable y tampoco he encontrado razón alguna que justifique el por que noTriste. Más bien coincido con esta opinión.

http://stackoverflow.com/questions/9397528/server-sent-events-vs-polling.

Y porque me estoy preocupando yo por esto, sencillo todo fue a raíz de este post del amigo @Marc_Rubino y mis comentarios junto a los de los amigos @panicoenlaxbox y @gulnor

Seguridad en aplicaciones SPA.

Si os habéis molestado en leer los comentarios veréis que en uno de ellos yo defiendo la usabilidad más que evidentemente la seguridad que creo que todos los que intervinimos estamos de acuerdo en que este no debe de ser tu mecanismo de seguridad.

Con lo cual vamos manos a la obra y defender con código como a mí me gustaría encontrarme una aplicación.

Para ello lo primero que voy a hacer es plantear que existen dos usuarios (UsuarioA y Admin).

En un principio el UsuarioA no tiene permisos para entrar a una serie de páginas pero a lo largo de la mañana el usuario Admin le da permisos por un momento y se los revoca una vez realizada una tarea.

La pregunta es sencilla os gustaría ser el UsuarioA y entrar a esa opción de la App, pasarte un rato rellenando información y cuando pulses guardar que te diga tu app que no tienes permisos, yo creo que no.

Yo más bien y poniéndome en la piel del UsuarioA, prefiero que me diga directamente que no tengo permisos y no hacerme perder el tiempo.

Para ver todo lo que hay que hacer he creado una SPA con MVC y Angularjs y veréis que se puede hacer para luchar con el problema de la cache en una SPA.

Aplicación de Servidor(MVC).

Aunque os voy a dejar el código completo de la app vamos a explicar el porque de las cosas.

1. Nos encontramos con un controlador “HomeController” donde podemos observar una diferencia notable la acción Index no devuelve un PartialView sino directamente un View. Y por que?.

Sencillo, este es el marco principal del Spa y me sirve para aprovechar la potencia de Razor para insertar en JavaScript algún que otro dato.

image

Como podéis ver en el código. Es en la vista Index donde configuro las rutas que después vamos a consumir en nuestros módulo de Angularjs.

Los motivos son claros.

1. Me permite crear el JavaScript dinámico en función de la información del usuario.

2. Puedo utilizar Url.Action en el servidor para indicar cual es la ruta de mis templates, que algún que otro susto me he llevado con no utilizarlo en MVC.

Los controladores como ya os he dicho devuelven todos PartialView(que a la postre son mis templates de Angular,pero aprovechando la potencia de Razor) excepto el Index.

image

Como podéis ver existen un par de controladores a tener en cuenta aparte de los ya comentados.

Forbidden: Si el usuario no tiene permisos para ver una página se le mostrará este html.

<h2>No tiene permisos para ver la página</h2>

NotFound: Si el usuario va a una ruta que no existe desde angular no me gusta redirigirlo a “/” sino a una página que le diga que no existe.

<h2>La página no se encuentra</h2>

Lo siguiente es hacer un par de modificaciones en el Layout Principal de nuestra app.

1. Agregar un enlace al modulo de Angularjs.

<body ng-app=»myApp»>

ngApp

2. Modificar nuestro menu para apuntar directamente a las rutas de Angularjs.

image

3. Si no estoy autenticado para que quiero cargar Angularjs y el resto de JavaScript innecesario.

image

Si yo siempre y por principios Sonrisa voy autenticar contra un servidor y para ello voy a utilizar una vez autenticado el clásico patrón.

http://www.variablenotfound.com/2009/11/el-patron-post-redirect-get.html

http://es.wikipedia.org/wiki/Post/Redirect/Get

Vamos que veo un peligro y no pequeño en poner en mi código JavaScript algo como esto.

if(login()) {….} 

Web Api

Necesitamos crear dos métodos en un controlador.

1. Un GET que es el que se encarga de hacer la magia de Server-sent DOM events.

image

Si observamos este código podemos ver que se inicializa un ManualResetEvents y espera hasta que el administrador cambia los permisos en el siguiente método.

2. Otro controlador PUT que es desde donde el Administrador cambiará los permisos de la aplicación(Ojo que en el código no esta contemplado nada a nivel de roles).

image

Como podéis ver se llama al método Set del ManualResetEvent y se inicializa a un nuevo objeto.

3. Un MediaTypeFormatter que nos permite devolver al cliente un contentType con valor igual a text/event-stream.

image

Y por último vamos a describir que es lo que hemos hecho en el módulo de Angularjs.

Esta no es la forma correcta de organizar el código JavaScript, pero espero que me perdonéis que no es más que una demoSonrisa.

image

1. Definimos un módulo.

var app = angular.module(«myApp», [«ngRoute»]);

2. Configurar nuestra app.

Primero cargamos todas las rutas que previamente configuramos en nuestra página Index(Razor).

Definimos un decorator para ampliar funcionalidades a $routeProvider (concretamente chequear si el servidor nos informa con un cambio de permisos y refrescar la pagina del cliente).

image

3. Una constante. Que es el identificador de evento asignado al usuario por parte del servidor y que se pasa como parámetro al método GET del controlador de nuestra Api.

4.Un servicio que es el que se encarga de toda la magia de Server-sent DOM events.

image

Si nos fijamos en el código observamos lo siguiente.

1. Se le inyecta a esta función el $routeProvider y la constante event.

2. Creamos al señor de la discordia EventSource.

3. Nos suscribimos al evento del usuario y es aquí una vez que nos llegue la información del servidor la convertimos a Json y chequeamos la ruta con el método checkRoute que previamente incluimos en el config del módulo, concretamente en el decorador de $routeProvider.

4. Por ultimo exportamos las funciones connect y close(una vez que el usuario hace logout se desconecta del servidor.

Si el usuario cierra la pestaña del navegador se cancela automáticamente.

4. Controladores. Poco tienen que explicar… Uno se encarga de cambiar los permisos del usuario y el otro de llamar al método close del apartado anterior.

5. Run. Una vez que se ejecuta nuestro módulo se ejecuta esta función y se llama a suscribeEvents.connect().

image

Pues señores que nos falta simplemente ver esto en ejecución para ello desde Firefox voy a hacer de Administrador y desde Chrome seré el usuario agraciado.

Siento no poder hacer la demo con IE11, pero los motivos son obvios.

image

Una vez que el señor Administrador pulsa en el botón Cambiar Permisos y transcurridos 5 segundos( Thread.Sleep(5000) en el servidor ) el señor “UsuarioA” ve el contenido de la página.

image

Si abrimos cualquiera de las peticiones de la carpeta NetWork podemos ver lo siguiente.

image

El Accept que envía el cliente  con valor text/event-stream y la respuesta del servidor Content-Type con valor text/event-stream.

Código de la aplicación completo

Conclusiones.

Muy breve, simplemente es una pena que de una vez por todas esta lucha sin cuartel de los exploradores no se acabe y que siempre tengamos que luchar con este tipo de cosas, que desde mi punto de vista no conducen a nada.

Por último ya sabéis cual es nuestro destino para escribir de Angularjs  desarrolloweb y espero que esto solo sea eso un aperitivo de lo que vais a ver.

Gracias!!!!

Observable o dirty, menos lobos caperucita.

Ni que decir tiene que hacer  Binding con Observable sobre el papel debe de ser más rápido que utilizar Dirty . Pero cuando uno analiza un Binding se debe de preocupar de muchas cosas y la más importante y la que quizá de mejor experiencia de usuario es la carga inicial y por tanto el uso inmediato de nuestra página.

Todos sabemos que existen varios tipos de binding. Aunque en este post solo voy a hacer énfasis sobre OneWay.

1. OneWay: actualiza la propiedad de destino únicamente cuando cambia la propiedad de origen(descripción copiada íntegramente de la página de msdn).

Y porque solamente este, pues sencillo porque voy a hacer medidores de rendimiento de tres frameworks JavaScript y en ningún caso me va a preocupar el rendimiento de TwoWay, por malo que sea, será inapreciable por parte del usuario.

Yo personalmente siempre he utilizado Dirty para hacer Binding y jamás me he preocupado del rendimiento en ese aspecto y también puedo decir que ningún cliente se me ha quejado de esto.

Bueno la verdad es que miento, alguna vez que otra he desarrollado una app con xaml y he utilizado Observable y esa magnífica interfaz llamada INotifyPropertyChanged. Vamos mi amada.

Lo que también digo es que en todos esos desarrollos he terminado por poner un if y la definición de un flag para evitar que se actualizara algo en mi property set, de eso puedo dar fe, sobre todo cuando utilizamos TwoWay.

Dicho esto os voy a decir que tres Framework JavaScript voy a analizar.

Angularjs: Utiliza dirty.

Knockoutjs: Utiliza observable

Durandaljs: Utiliza knockoutjs para los binding y por tanto es observable.

Una cosa que os recomiendo es esta lectura Databinding in angularjs puesto que describe a la perfección el patrón ENGAÑABOBOS (Muestra a los humanos lo que sus ojos pueden ver y despreocúpate del rendimiento,puesto que es implícito cuando aplicas esta técnica ).

Para ver realmente las bondades de observable y dirty os voy a proponer el siguiente supuesto,requisito,etc.

Una empresa nos encarga un software y tenemos que hacer una sencilla página.

Un vendedor está en la calle y se dedica entre otras cosas a la gestión de cobros. Cuando va a un cliente le tienen que aparecer las facturas pendientes de cobro y un botón para cambiar el estado, damos por hecho que las cobra todas,para no complicar el ejemplo que al final es de lo que se trata.

Todas estás pruebas las he hecho simplemente con las herramientas de desarrollo de Google Chrome.

Chrome DevTools Revolutions 2013.

Voy a hacer medidores de tiempo para el siguiente numero de registros.

100. Que alguien te deba 100 facturas  y que lo permitas ya es complicado.

El resto de mediciones las voy a realizar sobre 500,1000,5000 y 10000 simplemente por ver la capacidad de estrés de estos tres Frameworks.

En estos medidores voy a tomar tiempos del Binding,Completed y Program.

Binding: Tiempo en ejecutar el binding y no renderizar, que es lo realmente preocupante.

Completed: La ejecución integra del StackTrace o CallStack.

Program: Aunque no he logrado encontrar documentación al respecto,parece estar ligado a la liberación completa por parte del explorador después de ejecutar Completed y por tanto el momento exacto en el que el usuario puede volver a interactuar con nuestra página. Pero que a diferencia de Binding y Completed que van juntos de la mano puesto que se ejecutan en la misma Pila de llamadas (un usuario pulsa un click y por tanto se ejecuta binding). Este parece ser otro thread que hay que sumar a completed.

Para calcular el binding de cada librería he implementado un sencillo tomador de tiempo y he modificado las librerías originales para insertarlo en el punto donde se inicia el binding y de esta forma comprobar que la herramienta Chrome era fiable.

image

Por cierto alguien ha dicho que no se puede implementar en Prototype Pattern miembros privados, es que me ha parecido haberlo escuchado un montón de veces. ¿Es cierto? Lengua fuera.

Dicho todo esto os voy a mostrar un ejemplo con Angularjs para que veáis como he tomado los tiempos.

image 

Como bien podéis observar por el numero de la consola escrito con mi medidor de tiempos ese 94 coincide con el 93,21 mostrado en el Binding. Para ser exactos hay una diferencia de 0,79 milisegundos que me confirman que estoy tomando bien los tiempos del Binding.

Por resumiros un poco todo si os fijáis creo un nuevo clock antes de llamar a scope.$Apply que es la función que a su ves llama $rootscope.$digest  que ejecuta el famoso DIRTY de Angularjs.

finally

Y porque “Evitar su uso” pues sencillo el propio Chrome la marca como función no optimizada de JavaScript.

Menos mal que Angularjs está desarrollada, apadrinada por el propio Google.

Dejando al margen a Angularjs del que os prometo capítulos muy interesantes a lo largo del 2014, vamos a pasar a ver números de los tres frameworks.

Angularjs

image

Knockoutjs.

image

Durandaljs.

image

Analisis de la información.

Observamos que efectivamente Observable es más rápido que DIRTY pero con unas diferencias muy pequeñas que nunca pasan de los 500 milisegundos en casos extremos.

Pero también podemos ver una cosa muy significativa y es que un Binding no es lo más importante, al final lo verdaderamente costoso es la renderización y es aquí donde Angularjs gana por goleada en cuanto a experiencia de usuario se refiere.

Jamás pasa de los 5 segundos mientras que Knockoutjs y por tanto Durandal superan los 20 segundas en situaciones de estrés extremo, incluso en las pruebas he llegado a ver 38 segundos por parte de Durandaljs. Me temo que por un abuso de promise (Que esto no es c# que es JavaScript).

Que  no es bueno poner muchos.

setTimeout(function(){},1);

Para quien quiera verlo le recomiendo revisar el archivo system.js y dentro de este la función acquire del objecto system.

image

Vamos que deja los task  para c# y piensa que el JavaScript es síncrono excepto en cuatro cositasSonrisa.

Pero hay un dato muy significativo que quizás no habéis observado y es la poca diferencia que hay en el Binding de Knockoutjs y Durandal para el Load y el Update(cambiar el estado de las facturas) y es que los tiempos se parecen, entonces es que estoy poniendo un ejemplo malo para ver la potencia de Observable frente a Dirty. Pues noooooooo.

Lo que pasa es que he tuneado Knockoutjs  para que la carga sea más rápida utilizando Mustachejs, porque sino señores ni con Observable cualquiera de los otros dos frameworks es más rápido en ninguna situación.

Simplemente por llevar la contraria a mi gran amigo @gulnor (Juanma) si hombre el bueno de Koalite. He hecho que los Bindings en Knockoutjs  sean menos VERBOSE Sonrisa gracias a Mustachejs.

Es decir de tener que hacer un curso de ingeniería para hacer un Binding o más bien acordarte por donde vas.

He convertido esto.

image

En esto otro y que funciona en Knockoutjs   gracias a este otro script ko.mustache.js vamos y el tío se pone Copyright XDDDDD. Por implementar estas cuatro líneas.

image

Más agregar esta línea de código.

ko.setTemplateEngine(new ko.mustacheTemplateEngine());

La verdad que es libre de poner Copyright o lo que le apetezca pero el caso es que de lo VERBOSE pasamos a algo más agradable.

image

A costa de evidentemente mejorar el Load de la página y penalizar el único sentido de Observable.

Con lo cual mejoramos el binding gracias Mustachejs y no Knockoutjs  en el Load y lo penalizamos en el Update. Puesto que aquí Knockoutjs pierde la noción del tiempo y ya no sabe que es lo que realmente ha cambiado y vuelve a renderizar todo.

En definitiva que de este análisis sacamos una cosa clara y es que por mucho que me cuenten no es el Binding lo más importante sino la situación completa es decir “desde que el usuario pulsa un boton hasta que obtiene sus resultados”. Y es en esto donde AngularJs gana por goleada.

Con lo cual si utilizamos lo propuesto por Knockoutjs  === Xaml nos vamos de los 4040 milisegundos en la carga a los 8800 utilizando el método no VERBOSE.

Y en el Update===Observable bajamos de los 6718 milisegundos a 248 milisegundos frente a los 265 milisegundos que tarda Angularjs en cambiar el estado de los 10000 registros.

Vamos que ni con esas ganan en situaciones extremas y si a eso le sumo el código que necesito en uno y otro sitio como que me quedo donde estoy y espera a verlas llegar. Es decir me quedo con Angularjs y todos los días mi obligación es estar al tanto de que es lo que mejor y hasta el momento se quien es. Os presento al ganador de la batalla.

image

 

Por último os muestro el código de Angularjs Durandal con el que he hecho las pruebas, aunque si alguno lo necesita que lo pida por twitter y se lo mando.

Angularjs.

image

Durandaljs

image

Madre mía ese ko.observable y ese ko.observableArray hace que entre en éxtasis y desenfreno pensando que llamo a PropertyChanged de c#. Pero el máximo es la siguiente línea para modificar el estado de las facturasSonrisa .

oldvalues[i].state(!oldvalues[i].state());

No os parece más normal esto en Angularjs.

image

que no esto otro en Durandal/Knockoutjs.

image

Que no hemos acabado, que viene lo mejor, que es mi critica personal a ambas librerías(Angular Knockoutjs) por no tener en cuenta lo que los viejos guerreros llamamos fuente de la sabiduría.

Es decir no hacerle caso a este señor, cuando lo que vas a leer y ver está soportado desde el principio de los principios en todos los browser y que no es otro que DOM DocumentFragments.

Vamos para los desarrolladores de componentes entre los que me incluyo el DoubleBuffered de toda la vida.

Construye y lee poquito y  cuando todo esté listo pinta y no lo hagas a pasitos que las pantallas parpadean.

Bueno como uno de los lemas de JavaScript es el Duck typing y el otro es si eres desarrollador de JavaScript eres un Duck(cua,cua y guarro como tu solo). Yo me he convertido en patito y os voy a demostrar como con un código guarro y pensado para lo que es gana y por goleada.

Es decir un lenguaje de Script y que solo se debe de utilizar para esto y en esto es bueno, os voy a demostrar como esos segundos los bajo a milisegundos. Es decir voy a pasar de casi los 20 segundos de Knockoutjs y los 5 segundos de Angularjs. A …. “replique de tambores” ¿Cuantos? 3 segundos, no 820 milisegundos  y lo puedo baja más.

Pintar en el Browser una lista con 10000 elementos.

image

Venga os paso el html y JavaScript a ver si os convierto en patitos, que por cierto bastante guarros son. Y sino regalar a los niños uno por reyes y veréis como acaba el Jardín.

Html .

image

JavaScript.

image

Venga os copio la vista completa, que solo sea copiar y pegar Sonrisa

   1: @{

   2:     ViewBag.Title = "DocumentFragment";

   3: }

   4:  

   5:  

   6: <script id="template" type="text/template">
   1:  

   2:     {{#invoices}}<li><span>{{id}}-{{description}} Dicen que soy lento pero están equivocados,SOY: <strong>{{who}}</strong> | estado : {{state}}</span></li>{{/invoices}}    

</script>

   1:  

   2: <button onclick="render();return false;">Ejecutar como un spa</button>

   3: <button onclick="clearlist(); return false;">Limpiar Lista</button>

   4: <ul id="list">

   5: </ul>

   6:  

   7: @section scripts{

   8:     

   9:     

  10:     

  11:     <script src="~/Scripts/mustache.js">

   1: </script>

   2:     <script>

   3:         var render = function () {

   4:             var fragment, i, templateText = document.getElementById('template').innerText.trim(), clock = util.clock();

   5:             list = document.getElementById('list'),

   6:             createInvoices = function () {

   7:                 var array = [], i, invoiceNumber;

   8:                 for (i = 0; i < util.records; i++) {

   9:                     invoiceNumber = i + 1;

  10:                     array.push({ id: invoiceNumber, description: "Invoice:" + "(" + invoiceNumber + ")", who: 'DocumentCreateFragment', state: false });

  11:                 }

  12:                 return array;

  13:             },

  14:             array = createInvoices(),

  15:             html = Mustache.to_html(templateText, { invoices: array }),

  16:             element = document.createElement('div');

  17:  

  18:             element.innerHTML = html;

  19:             fragment = document.createDocumentFragment();

  20:             for (i = 0; i < element.childNodes.length; i++) {

  21:                 fragment.appendChild(element.childNodes[i]);

  22:             }

  23:  

  24:             list.appendChild(fragment.cloneNode(true));

  25:             console.log(clock.writeTime());

  26:         },

  27:         clearlist = function () {

  28:             list = document.getElementById('list');

  29:             list.innerHTML = "";

  30:         };     

  31:     

</script>

   7:  

   8: }

   9:  

  10:  

Así que hoy hemos visto el patrón CUAC-CUAC.

Es aquel que dice que para pasar de un entorno a otro lo primero es preocuparte de conocer el nuevo y una vez hecho esto no hay otra cosa más sencilla que elegir lo más apropiado, pero si eres desarrollador de c# y piensas que pasar a JavaScript es utilizar TypeScript y los frameworks que más se asemejan a mis conocimientos lo tienes claro, es más no claro la leche que te vas a dar va a ser no pequeña sino muy grande.

Con lo cual musa mía @jersiovic aplícate lo leído y dile al ausente del @pacomonfort que me debe una consultoría que me va a pagar con unos gustoso G….tonic y PAGINA.

Colorín, colorado. Que bien que hemos empezado el Año.

Va por ti “Manolete” que tu si que eras grande.