viernes, 21 de abril de 2017

Empate técnico: React vs Angular 2

Lo primero que hay que tener claro es lo siguiente: React de por sí NO es un framework y Angular 2 SÍ. Pero, entonces, ¿por qué compararlos? son dos formas de trabajar y de hallar soluciones con un nexo en común: un modelo de interfaces basado en componentes.

NOTA: Este artículo lo publiqué originalmente en: https://medium.com/@FerCortesF/el-empate-en-las-caracter%C3%ADsticas-de-react-y-angular-2-c18f9c0c9247

Comodidad vs Libertad

Para mí, este punto es un empate. Angular  2 aporta la comodidad de tener un framework completo que dispone de todas las herramientas necesarias para llevar a cabo una aplicación, permitiendo que te despreocupes de tener que buscar la forma de implementar funcionalidades, lo que hace que todo tengas que hacerlo "a su manera". Mientras que React aporta la flexibilidad y la libertad de seleccionar las herramientas con las que deseas trabajar, aumentando así la carga de decisiones tecnológicas que tomar. Esta filosofía permite ir reemplazando librerías que permitan soluciones más modernas para resolver problemas conocidos.

El DOM Virtual

Algo que me gustó mucho de React cuando oí hablar de él fue el DOM Virtual. Una solución muy interesante para resolver problemas de rendimiento en frameworks anteriores (como Angular 1.x). El DOM virtual es una representación del DOM que permite comparar el DOM activo con el nuevo DOM resultante. Gracias a esta comparación se puede establecer el número menor de instrucciones que transformaran un DOM en otro.

El DOM Virtual supone una gran ventaja en rendimiento con sus predecesores, pero por su parte, el equipo de Angular 2 ha trabajado duro en mejorar el rendimiento de su interacción con el DOM. Se ha acercado mucho al rendimiento de React y ha mejorado sustancialmente el rendimiento de Angular 1.x. En este punto la ventaja es de React, aunque no es una ventaja muy grande.

Benchmarks: https://auth0.com/blog/more-benchmarks-virtual-dom-vs-angular-12-vs-mithril-js-vs-the-rest/ Destacar del artículo que hay otros frameworks y librerías con un rendimiento increíble. Habrá que estar atentos al trabajo de "incrementaldom" por parte de Google.

Pruebas "en vivo" de tasa de refresco en pantalla: http://mathieuancelin.github.io/js-repaint-perfs/

Plantillas

Una diferencia muy destacada e importante es el lenguaje en el que están centradas las plantilla de uno y otro. Angular 2 centra sus plantillas en HTML, mientras que por su parte React centra sus plantillas en Javascript (con el motor JSX). Para leer  de Angular 2 debes conocer sintaxis específica de Angular y en React sólo debes conocer Javascript.

¿Qué es eso de centrado en HTML o Javascript? En Angular 2 escribimos cierta lógica en el HTML, trasladando javascript a HTML. Mientras que con React toda la lógica permanece en javascript, es decir, se traslada HTML a javascript. La ventaja es tener un código mucho más centralizado y legible, pudiendo identificar los fallos de una forma más eficaz y pudiendo aplicar toda la potencia de javascript a nuestras plantillas.

En este punto hay una pequeña ventaja para React.

Detección de errores en las plantillas

Otro punto importante es la detección de fallos en las plantillas. Angular 2, al igual que Angular 1.x tiene un problema, la detección de errores en una plantilla se produce en tiempo de ejecución, aportando además una información poco determinante para encontrar el error. Por su parte, en React el fallo se detecta durante la compilación de la plantilla, aportando información acerca del error y la línea que provoca el error.

En este punto la ventaja es para React.

Typescript!

Aunque no soy el mayor fan de Typescript, creo que proporciona un camino muy claro de cómo deben hacerse las cosas con Javascript, ayudando en proyectos de gran envergadura al trabajo en equipo de una forma clara y concisa. Angular 2 ha sido creado con Typescript y pensado para usarlo junto con Typescript, lo que supone una gran ventaja. También existe la posibilidad de usar React con Typescript, pero la siombiosis entre ambos no es tan clara como la que hay con Angular 2. 

En este punto la ventaja es para Angular 2.

Web Components

Tanto Angular 2 como React están orientados al desarrollo de componentes reutilizables. Sin embargo, Angular 2 toma una ligera ventaja por un motivo: sus componentes son más fácilmente convertibles en componentes web nativos.

Estabilidad

Como se ha comentado antes, React permite mayor libertad en la elección tecnológica. Sin embargo, esto conlleva una desventaja a mi parecer: La estabilidad. Aunque React, es un producto de una estabilidad importante, debemos tener mucho cuidado al elegir el Stack tecnológico restante. Si utilizas muchas librerías complementarias, la sensación de que algo pueda quedar "deprecated" es más común y hace que en algunos casos deba reescribirse código.

Por su parte, Angular 2 al ser un framework completo, es complicado que tras una Release exista un cambio desproporcionado que nos haga tener que reescribir nuestro código. Además, Angular 2 tiene todo un equipo detrás tomando decisiones sobre el futuro, que en caso contrario deberías tomar tú.

Ventaja para Angular 2.

Herramientas de trabajo

Para trabajar con Angular 2 cualquier IDE que soporte HTML/JS puede ser de utilidad. Si deseas trabajar con React necesitarás un IDE que soporte JSX. Sin embargo, dada la popularidad actual de React, los IDEs modernos tienen soporte para JSX. Por lo tanto en este punto existe un empate.

Flujo de datos unidireccional

El flujo de datos unidireccional es una ventaja muy importante. Es uno de los puntos débiles de Angular 1.x. Imagina cómo de confuso e ineficiente puede ser una sincronización de datos con la vista  que está ocurriendo al mismo tiempo que una sincronización de la vista con los datos.

Tanto Angular 2 como React utilizan mecanismos para que el flujo de datos sea unidireccional. En React sólo se dispone de la posibilidad de enlazar los datos en una dirección, obligando de esta forma a que el flujo sea siempre unidireccional. Por su parte en Angular 2, aunque existe una forma de enlazar los datos en dos direcciones, internamente el flujo de datos funciona en una sóla dirección. Por lo tanto, Angular 2 encapsula eficientemente el enlace bidireccional de datos, aunque pueda resultar confuso.

Empate.

Conclusión

Existen otras características que podrían compararse: el tamaño de uno frente al otro, la curva de aprendizaje de uno y de otros, facilidad para empaquetar un proyecto, etc. Sin embargo, serían comparaciones menores que acabarían dandonos un resultado muy similar al actual. En la comparativa actual Angular 2 toma ventaja en 3 puntos, React tiene ventaja en 3 y otros 3 en empate.

Comparar Angular y React técnicamente, aunque interesante, es como comparar dos coches equivalentes a raiz de la tecnología interna de su motor de combustión. Finalmente, es probable que decidas por un coche u otro en base al color.
http://blog.angular-university.io/why-angular/

En el artículo http://blog.angular-university.io/why-angular/ me gusta mucho la explicación de por qué nunca hay una razón de peso para elegir uno u otro: "Tanto Angular 2 como React, se centran en resolver principalmente los mismos problemas. La diferencia radica en diferentes aproximaciones técnicas internas para resolver estos problemas. Sin embargo, en los dos, tanto las características como los patrones de diseño utilizados se superponen mucho". Por lo tanto, el empate técnico está más que justificado. Yo personalmente he elegido el color de React, puedes verlo en https://medium.com/@FerCortesF/reflexi%C3%B3n-por-qu%C3%A9-eleg%C3%AD-react-antes-que-angular-2-117becd22699#.8nx7o58d9

Bibliografía

martes, 9 de junio de 2015

Apache Cordova: Descarga y visualización de PDFs

A partir de un caso de uso real me surge la necesidad de descargar PDFs y visualizarlos en una aplicación multiplataforma llevada a cabo con Apache Cordova y AngularJS. Para probar cómo podía llevarlo a cabo realicé un pequeño ejemplo en el que se descarga y almacena un PDF en la aplicación y su visualización.

Puedes descargar el código aquí 

Objetivo

Aplicación de Apache Cordova junto con AngularJS que permita descargar al dispositivo un PDF y visualizarlo.

Plugins necesarios

  1. InAppBrowser https://github.com/apache/cordova-plugin-inappbrowser
  2. File https://github.com/apache/cordova-plugin-file
  3. FileTransfer https://github.com/apache/cordova-plugin-file-transfer
  4. Device https://github.com/apache/cordova-plugin-device

Utilidad

  1. InAppBrowser - Permite la apertura de nuevas ventanas del navegador, incluso permite la llamada al navegador del sistema.
  2. File - Rutas del sistema de archivos.
  3. FileTransfer - Descarga del PDF.
  4. Device - Conocer en qué plataforma estamos trabajando.

Compatibilidad

  •  Android
  • iOS
* Los plugins utilizados son compatibles con Windows Phone y Windows 8  por lo tanto habría que estudiar este caso e implementarlo para estas plataformas.

Singularidades de plataforma

En este desarrollo entra en juego algunas singularidades de cada plataforma que limitarán la universalidad de la aplicación.

Android

  • En Android NO es posible abrir una ventana nueva dentro de la aplicación que muestre el PDF.
  • Permite abrir PDFs desde el navegador del sistema (permitiendo abrirlos desde una aplicación como Adobe Acrobat).
  • SI dispone de carpetas en las que el contenido puede ser accedido desde otras aplicaciones

iOS

  • En iOS SI posible abrir una ventana nueva dentro de la aplicación que muestre el PDF.
  • Permite abrir PDFs desde el navegador del sistema (solamente abre el documento desde el navegador del sistema).
  • NO dispone de carpetas en las que el contenido puede ser accedido desde otras aplicaciones

Solución

Se mostrará 1 botón de descarga del PDF y una vez descargado el PDF aparecerá un botón para su visualización. La aplicación de ejemplo dispondrá de una sola vista, un controlador y dos servicios de AngularJS. Uno de los servicios se encarga de la descarga y almacenamiento del PDF mientras que el otro servicio se encarga de la visualización del PDF. 

En ambos servicios de AngularJS se emplea el objeto $document para poder suscribirse al evento deviceready y así acceder a los plugins de Cordova después de que se hayan cargado.

Debido a las singularidades de Android el documento se descargará en una carpeta compartida de la aplicación y se visualizará llamando al sistema. Es necesario descargarlo en una carpeta compartida para que otro programa pueda acceder a él.

Por otro lado en iOS el documento se almacenará en una carpeta que no sea eliminada por el sistema y se mostrará en una ventana dentro de la aplicación. En este caso se visualiza dentro de la aplicación debido a que en iOS no podríamos compartir nuestro documento con otras aplicaciones.

En resumidas cuentas solamente dos parámetros deberán ser dependientes de la plataforma: 
  • La ruta de almacenamiento
  • La forma de abrir el PDF

Rutas de almacenamiento

En la documentación del Plugin File se encuentran tablas en las que se explica qué rutas son "limpiadas" por el sistema operativo, cuáles son de lectura/escritura y cuáles son privadas.

Para esta aplicación de ejemplo se han elegido las siguientes rutas del plugin que cumplen las condiciones deseadas:
  • Android: cordova.file.externalApplicationStorageDirectory
  • iOS: cordova.file.dataDirectory

Apertura del PDF

En la documentación del Plugin InAppBrowser se especifican los valores del parámetro que indica la forma en la que se abrirá la nueva ventana. 

Para esta aplicación de ejemplo se han utilizado los siguientes valores del parámetro para cada plataforma:
  • Android: _system
  • iOS: _blank

Resultado


Resultado en Moto G:
 

 

Resultado en iPhone 5:
 


lunes, 13 de abril de 2015

AngularJS - Introducción: Inyección de dependencias

La Inyección de Dependencias(DI - Dependency Injection) es un pilar fundamental de AngularJSy es que la DI se relaciona con la forma en la que se hacen referencias desde el código, pero, ¿qué es? La Inyección de Dependencias es un patrón de diseño orientado a objetos. Este patrón nos dice que los objetos necesarios en una clase serán suministrados y que por tanto no necesitamos que la propia clase cree estos objetos. 

El framework de AngularJS  gestiona la inyección de dependencias, por lo tanto, nuestras dependencias, como por ejemplo de servicios, serán suministradas por AngularJS. Por ello, al crear un componente de AngularJS se deben especificar las dependencias que esperamos y será el propio framework el que nos proporcione los objetos que se solicitan. Por ejemplo, si necesitamos utilizar un servicio en un controlador, al crear el controlador debemos especificar nuestra dependencia del servicio y no intentar crear un objeto del servicio.
En este ejemplo se puede ver que el controlador tiene dos dependencias para funcionar. El $scope (ámbito) y un servicio que se ha creado previamente. Por lo tanto, en el controlador se esperan como parámetros el $scope y el personaService. Será el framework el encargado de proporcionar el ámbito correspondiente al controlador y el objeto de servicio esperado.

¿Qué significa el array que aparece después del nombre del controlador?  Esto no es solamente para crear un controlador, si no que cualquier componente que creemos en AngularJS puede/debe llevarlo (no es la única manera de llevarlo a cabo). Este array sirve para indicar las dependencias cuando se minimice el código. Es por esto por lo que los primeros parámetros del array son las dependencias (como una cadena) y después la función del componente que se esté implementando.

Aprendiendo AngularJS

viernes, 10 de abril de 2015

AngularJS - Introducción: Módulos (Modules)


En los lenguajes de alto nivel por lo general se tiene una característica muy importante: la modularidad (por ejemplo: namespace en C# o package en Java). Sin embargo, ¿qué ocurre en Javascript con la modularidad? No existe, Javascript mantiene un espacio global para todo. ¿Qué implicaciones tiene esto? Que a medida que crezca nuestro código más sencillo es que se produzca una colisión de nombres de variables, funciones...

AngularJS pretende solucionar los problemas con la modularidad de Javascript. Para ello AngularJS implementa el concepto de módulos, permitiendo la asociación de recursos a cada uno de los módulos. De esta forma, el framework nos proporciona una herramienta para poder realizar un división lógica de responsabilidades.

¿Qué es un módulo? Un modulo es un objeto de AngularJS que actúa como contenedor de recursos y partes de la aplicación: Controladores, servicios, filtros, etc. [1]. En la siguiente imagen, obtenida de un vídeo de Dan Wahlin [2], se puede ver qué puede definirse en un módulo de AngularJS

Una de las funciones principales del módulo es la de configuración, dónde destaca particularmente la configuración de rutas de acceso a la aplicación. Por tanto, analizando la relación entre los componentes de AngularJS se puede obtener el siguiente esquema [3] (llevado a cabo por Dan Wahlin)

Para utilizar el módulo como aplicación la directiva de Angular ng-app espera el nombre de un módulo, será en este módulo dónde se buscarán los diferentes componentes de la aplicación. 

Para trabajar con los módulos, lo primero que se debe conocer es la forma de crearlos y de acceder a ellos:
  • Crear un nuevo módulo: angular.module('nombreModulo',[]);
  • Obtener la referencia de un módulo existe: angular.module('nombreModulo');
La única diferencia entre ambos métodos es que al crear el módulo se tiene un segundo parámetro que es un array. Este array sirve para especificar los nombres de los módulos necesarios para que funciones el módulo que estamos creando. Es decir, para especificar las dependencias. Un ejemplo claro es: dos módulos A y B en los que un controlador del modulo A necesitará un servicio del módulo B, por lo tanto al crear el módulo B:  angular.module('B',[]); Mientras que para crear el módulo A:  angular.module('A',['B']); Es decir, se especifica que el módulo A necesita el módulo B para funcionar.

En código se puede ver más claro



Aprendiendo AngularJS

[1] https://docs.angularjs.org/guide/module
[2] https://www.youtube.com/watch?v=rAyEGv67P-U
[3] http://weblogs.asp.net/dwahlin/video-tutorial-angularjs-fundamentals-in-60-ish-minutes

jueves, 9 de abril de 2015

Javascript: Jugando con fechas

A menudo ocurre que al escribir código obviamos ciertas características del lenguaje con el que se trabaja. Más todavía en Javascript dado que tenemos muchos caminos diferentes para llegar a un punto, por ejemplo, definiendo funciones[1]. En estos casos cada uno tiene sus manías para determinadas situaciones, pero al hacerlo podemos obviar ciertas especificaciones o comportamientos del lenguaje.

Debido a esto, el otro día me encontré con un problema en Javascript en el cuál se encontraban fechas, que no se habían modificado, con la fecha 1/1/1970. Las fechas venían en String por lo que era necesario transformarlas a objetos Date. Tras mucho depurar llegué a ver que en un punto (una directiva de AngularJS para fechas) estaba creando el objeto fecha de la siguiente forma: "new Date(fchEnString)" ¿Problema? Que no se comprobaba si el parámetro era null, aunque sí que existía una comprobación posterior acerca de si el objeto creado era una fecha válida. Sin embargo, existían casos en los que el parámetro no era String, era null, por lo que se estaba realizando un "new Date(null)" y esto devolvía una fecha válida: 1/1/1970. 




Por sí a alguno se le escapa el por qué del 1/1/1970: El estandar de ECMAScript[2] dice que en los objetos Date el tiempo es medido en milisegundos desde el 1 de enero de 1970 UTC.

"Date objects are based on a time value that is the number of milliseconds since 1 January, 1970 UTC" Mozilla Developer Network [3]

Una vez identificado y solucionado el problema me decidí a refrescar mis conocimientos de los objetos Date en javascript y preguntarme qué ocurría con otros valores para el objeto Date. Lo primero que hice fue buscar los constructores del objeto Date[4]. Encontré 4 formas de instanciar el objeto Date.
  • new Date();
  • new Date(milliseconds);
  • new Date(dateString);
  • new Date(year, month. day. hours. minutes, seconds. milliseconds);
Al ver los constructores y conociendo el por qué de 1/1/1970 pude deducir que: new Date(null) obtiene la misma fecha que new Date(0).



Lo siguiente que se me ocurrió fue utilizar undefined como parámetro en el constructor a ver si obtenía la misma fecha. Sin embargo, "new Date(undefined)" devuelve una fecha no válida.



Para probar un caso que fuese correcto, decidí escribir la siguiente cadena como parámetro: "2014/01/01".


Después busqué la forma de obtener la fecha actual (esta es fácil) con "new Date()".


A continuación, me pregunté cómo llegar a la fecha mínima en Javascript. Por lo que consulté la especificación [2]. En la especificación se establece que a partir del 1/1/1970 se pueden obtener fechas de hasta 100mil días más y 100mil días menos. 

"... The actual range of times supported by ECMAScript Date objects is slightly smaller: exactly –100,000,000 days to 100,000,000 days measured relative to midnight at the beginning of 01 January, 1970 UTC. This gives a range of 8,640,000,000,000,000 milliseconds to either side of 01 January, 1970 UTC."

Es decir, el valor mínimo de una fecha es "new Date(-8640000000000000)"

Por último, decidí hacer un par de pruebas con la función Date.parse()[5]



Referencias

[1] http://embat.es/post-tecnico/javascript-definir-funciones-ese-dolor-de-cabeza.html
[2] http://www.ecma-international.org/ecma-262/5.1/#sec-15.9
[3] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date
[4] http://www.w3schools.com/jsref/jsref_obj_date.asp
[5] http://www.w3schools.com/jsref/jsref_parse.asp

martes, 17 de marzo de 2015

AngularJS - Introducción: Servicios

La idea de AngularJS es mantener los controladores para el enlace de datos y reducir su carga lógica, es decir, AngularJS pretende separar responsabilidades. Por lo tanto, los encargados de tareas comunes y de la mayor parte de nuestra lógica de negocio con los datos serán los servicios. Formalmente, los servicios son objetos singleton que realizan tareas comunes a varias partes del sistema.

Esto se puede ver fácilmente con un ejemplo. Necesitamos obtener información mediante una petición HTTP. La petición podría realizarse desde el controlador, sin embargo, esto consigue un código excesivamente acoplado y sin ninguna separación entre responsabilidades. Si la petición HTTP la sacamos del controlador y la encapsulamos en un servicio habremos conseguido separar las responsabilidades. Es más, si deseamos realizar la misma petición desde otro lugar de la aplicación simplemente debemos utilizar el servicio que hemos creado.

En el siguiente ejemplo se puede ver un servicio que está siendo utilizado para obtener datos de personas y que se muestran en una lista:
Se puede ver el uso del servicio como origen de datos y además en el HTML la directiva ng-repeat que nos permite recorrer una lista de elementos en la vista y "pintar" el contenido de la etiqueta por cada elemento de la lista.

En este punto conviene destacar que servicio, factoría y proveedor a menudo se utilizan como intercambiables. De hecho los tres nos permiten realizar la misma funcionalidad y la misma separación de responsabilidades. La diferencia entre ellos radica en el nivel de configuración que se puede conseguir con cada uno de ellos.

Aprendiendo AngularJS

NOTA: el signo de '$' es utilizado por AngularJS como prefijo para servicios y objetos propios. Por lo tanto, es conveniente no utilizar el prefijo '$' al crear servicios

lunes, 9 de marzo de 2015

AngularJS - Introducción: Controladores

¿Qué es un controlador? AngularJS emplea el concepto de controladores como el elemento que define la funcionalidad de una parte de la página. Como aproximación se puede decir que un controlador responde a las acciones sobre una interfaz de usuario, lo que permitirá modificar la salida final por pantalla. 



Desde el controlador modificaremos el modelo de datos y gracias al enlace de datos que realiza AngularJS la interfaz de usuario se actualizará para responder al cambio del modelo. Es decir, el controlador utilizará el ámbito (Ver ámbitos) correspondiente para modificar los datos y de esta forma dar funcionalidad a la parte  de la página a la que afecte el controlador.

Lo explicado se puede ver de forma más clara en el siguiente ejemplo: En un controlador se define la variable* "nombre" en el ámbito con un valor inicial y además se define una función que vacía el contenido de la variable "nombre". Desde el HTML se utiliza la variable del ámbito "nombre" para generar un saludo y en la caja de texto al hacer click se llama a la función que vacía el contenido de "nombre".



NOTA: En el HTML los datos del ámbito se enlazan escribiéndolos entre corchetes {{}} (sintaxis moustache) o dentro de directivas 'ng-' o propias. Por ejemplo, ng-bind="nombreVariable" agrega el contenido de la variable a la etiqueta que utilice esa directiva. Otro ejemplo, es el de la directiva ng-click="algunaFuncion()" 

Aprendiendo AngularJS

*Aunque en estos ejemplos y en la mayoría de los tutoriales los datos se mandan en variables del $scope, lo ideal sería pasar los datos en un objeto Javascript y que este actué como modelo de datos. Esta es una práctica que no se tiene en cuenta muy a menudo y que si no se lleva a cabo podríamos  tener problemas con la jerarquía de ámbitos.