miércoles, 29 de agosto de 2018

Angular console - Introducción a la nueva consola de angular

La nueva consola de angular es una nueva interfaz con la que vamos a poder administrar nuestras aplicaciones de una manera más visual y eficaz.

Para empezar a usar la angular console, accedemos a la dirección https://angularconsole.com y nos descargamos la versión para nuestro sistema operativo (al momento de escribir esta entrada se trata de una version beta, aunque totalmente funcional y en general bastante estable, por otra parte no existe una versión para linux, pero es de esperar que este disponible en no mucho tiempo) .

Cuando arrancamos la aplicación veremos una pantalla, como la siguiente, donde nos da las opciones de crear un nuevo proyecto, importar o ver los proyectos recientes.



Vamos a ver como funciona la aplicación creando un nuevo proyecto.
Para ello pulsamos en el botón inferior que dice "Create" y nos aparecerá una especie de formulario donde nos preguntará:

1- Donde queremos crear nuestro espacio de trabajo (ojo!! no es el directorio de una aplicación especifica, sino un espacio de trabajo).
A diferencia del cli de angular, donde únicamente podemos crear una aplicación mediante el comando ng new, mediante la angular console podemos tener multiples aplicaciones en el mismo espacio, lo que sin duda es una enorme ventaja si por ejemplo tenemos un equipo de desarrollo trabajando en varias aplicaciones en el mismo espacio de trabajo.


2- Posteriormente daremos un nombre a dicho espacio de trabajo, en nuestro ejemplo le llamaremos AngularConsoleWorkSpace
3- Finalmente nos pedirá que schematics queremos usar, si la que trae angular por defecto en el cli, o la del fabricante de la aplicación, es decir la de nrwl (Narwhal Technologies Inc) y que veremos en entradas posteriores.

De momento vamos a seleccionar la del cli por defecto de angular y pulsamos el botón de Create.

Una vez que termina el proceso de creación, nos mostrará una pantalla similar a la que se muestra a continuación, donde podemos ejecutar los comandos que haciamos anteriormente con la cli, es decir, serve, test, build, generate componentes, entre otros




A la hora de arrancar el servidor con ng serve, en la configuración nos dará la opción de hacer seguimiento (watch) es decir de "regenerar" cada vez que haya algún cambio, o de usar compilación aot, el puerto, host, proxy, incluso de especificar si queremos la configuración por defecto o en modo producción, entre otras muchas más opciones:


Una vez que está configurado el comando serve pulsamos el botón de Run y nos levantará la aplicación con la configuración establecida en nuestro navegador especificado.

Tenemos otras tareas disponibles, para internacionalizacion, lint o testing, que podemos igualmente configurar, y que iremos viendo a lo largo de entradas posteriores.



En esta entrada de introdución a la angular console hemos visto que si bien el cli por defecto de angular es una herramienta estupenda, a partir de ahora tenemos la opción de gestionar nuestros proyectos (incluso a nivel monorepositorio con multiples proyectos en el mismo repo) mediante esta herramienta que promete mucho en un futuro próximo.

En entradas posteriores iremos viendo más funcionalidades de esta herramienta, y como usar la schematics de nrwl.





martes, 7 de agosto de 2018

Tipos parciales en TypeScript

En esta entrada vamos a ver que son las clases parciales en typescript.

Imaginemos un escenario relativamente habitual en el que declaramos una funcion como la siguiente:





esta funcion recibe como parametro un objeto de tipo CSSStyleDeclaration, que es una clase para definir una serie de estilos de un elemento del DOM como por ejemplo la alineacion, el background, alto, ancho etc. entre otras muchisimas propiedades.

La función unicamente muestra las propidades de dicho objecto.

Para llamar a la funcion, declaramos un objeto de tipo CSSStyleDeclaration:


Si quisieramos utilizar este objeto nos obligaría a definir todas las propiedades y asignarles "undefined", y son unas cuantas propiedades :-(
En la siguiente imagen podemos ver algunas de las propiedades que debemos asignar a dicho objeto:





De hecho, si nos fijamos en la siguiente imagen, el editor de código se "queja" de que no hemos asignado al objeto todas las propiedades de la clase CSSStyleDeclaration

Para estos casos podemos usar un tipo genérico que se llama Partial<T> el cual hace que al tipo de objeto que se aplica permite establecer el valor "undefined" a las propiedades que no han sido asignadas por nosotros.

Modificamos el codigo anterior para declarar los objetos como Partial

de esta forma el objeto styles tiene asignada las propiedades que hemos establecido y el resto
tienen el valor "undefined", y ya no se "queja" el editor.

En la declaración de la función tambien vamos a cambiar la declaración del parametro
para decirle que es "parcial"


Los tipos parciales como hemos visto pueden sernos de utilidad en determinadas circunstancias.

Codigo de ejemplo: https://gist.githubusercontent.com/jorgetrigueros/e99c8b14df99346774e8ec9bd70e8159/raw/ee43bc26b3e128622563b1b12df252fb3fd87c46/partialclass.ts




sábado, 5 de mayo de 2018

Creación de snippets en Visual Studio Code

Un snippet es un fragmento de código que puede ser reutilizado. Cuando programamos nos encontramos con ciertos bloques de código que se suelen repetir en distintas partes, incluso en distintos proyectos, (como por ejemplo sentencias de consola, bucles javascript, o en HTML para escribir el boilerplate de un documento). Normalmente este fragmento es de cierta utilidad porque nos soluciona un problema, que sin ser muy frecuente digamos que aparece de manera ocasional.

Podemos por tanto definir un snippet como "plantillas que facilitan la introducción de patrones de código repetitivos"

Un ejemplo de snippet es el que se usa para crear el boilerplate de HTML5 en el que basta escribir el símbolo "!" para obtener la estructura de un documento HTML5 "por arte de magia".


Visual Sutio Code almacena los snippet en ficheros json de tal manera que los snippet de javascript se encuentran en el fichero "javascript.json", los de typescript en "typescript.json" y así con el resto de lenguajes.

Para ver los ficheros disponibles para los distintos lenguajes podemos ir al menú Archivo > Preferencias > Fragmentos de código de usuario 



Esto nos mostrará un listado con los lenguajes disponibles:






En estos ficheros es donde añadiremos nuestros snippets. 

Un snippet es un objeto JSON que tiene las siguientes propiedades:

- nombre del snippet
- prefijo: prefijo con el que identificamos el snippet
- body: contenido del mismo.
- descripción: una breve descripción del snippet

Si seleccionamos un fichero, como por ejemplo el typescript.json, veremos que nos aparece una ayuda en forma de comentarios.

Vamos a crear un snippet a modo de ejemplo para entender mejor como se crean. 

Supongamos que queremos crear un snippet de una función que usamos de una determinada librería que hemos creado previamente. Esta función es un conector a un API y tiene la siguiente firma:

  connectorApi(message: string | Object, url: any, method: NuarHttpMethod,  applicationHeaders?: any,  timeout?: number,  async: boolean = false, isSecure: boolean = true): Observable<any>

El snippet que queremos crear es una llamada a este método al cual nos vamos a subscribir y mostrar un console.log para el next y otro para el error.

Podemos pensar inicialmente en algo así:


"connectorApi": {
"prefix": "custom",
"body": [ 
"this.myservice.connectorApi('https://jsonplaceholder.typicode.com/posts', 'GET', '').subscribe(",    "data => console.log(data),",
"error => console.log(error));"
],
"description": "Llamada función connectorApi"
}

Para usar este snippet basta con teclear el prefijo que hemos indicado, es decir  basta con teclear "custom" en visual studio code, lo que nos mostrará la información del snippet tal como podemos observar en la siguiente imagen:



 El problema del snippet anterior es que siempre nos va a colocar valores fijos. El servicio, la url del api, el tipo de llamada etc. y nosotros realmente lo que queremos es introducir esos valores en el momento de usar el snippet.

Para ello, podemos reemplazar los valores que queramos por:

$x: Situa el cursor en la posición para introducir un valor
${x:label} igual que el anterior pero colocando el valor definido en la propiedad "label"

Vamos a verlo mejor con el ejemplo anterior, pero especificando valores por defecto que pueda cambiar el usuario si lo desea. 


 "connectorApi": {
  "prefix": "custom",
  "body": [
   "this.${1:service}.connectorApi('${2:http://}', ${3:get}, '$4').subscribe(",
   "data => console.log(data),",
   "error => console.log(error));"
  ],
  "description": "Llamada función connectorApi"
 }

 Lo que hemos cambiado es el body del snippet. En concreto ahora el nombre del servicio toma como valor por defecto "service" (${1:service}) pero el usuario puede especificar el nombre de su servicio lógicamente.
Lo mismo para el parámetro de la url (${2:http://}) donde por defecto aparece como url "http://"
De forma similar especificamos el método que por defecto será "get" ${3:get} y para el último parámetro ($4) no hay valor por defecto y el usuario puede especificar el valor que desee.

Como anotaciones indicar que podemos especificarel mismo índice repetido en distintas posiciones, es decir, podriamos tener dos $1 en distintas posiciones del snippet (inicialmente según tecleemos un valor se asignará a todas las posiciones donde este el mismo índice, pero podemos cambiar independientemente el valor de las mismas para cada posición)

Por otra parte, si nos fijamos en el body del snippet, está formado por distintas líneas separadas por comas entre ellas, y donde cada línea debe ir entrecomillada.

El resultado de nuestro snippet es el que podemos observar en la siguiente imagen:


Los snippets nos pueden servir para no tener que escribir tanto código repetitivo, o al menos a facilitarnos en la medida de lo posible que no sea tan tedioso estar tecleando siempre las mismas porciones de código para determinadas funcionalidades.




jueves, 19 de abril de 2018

Resolvers en Angular

Un resolver es un código que se ejecuta después de hacer clic en un enlace, y antes de que se haya cargado el componente asociado a dicho enlace.

Básicamente el flujo que realizamos al usar un resolver es:

- Hacemos clic en un enlace que habremos definido en un router module.
- Se ejecuta el código asociado al resolver. El resolver puede devolver un valor (que puede ser un observable o cualquier otro tipo) al componente asociado al enlace sobre el que hemos hecho clic.
- Cargamos el componente pudiendo usar los datos obtenidos del resolver.

Vamos a ver un ejemplo donde mostraremos una serie de enlaces. Cada enlace va a representar un usuario. Al hacer clic en un usuario, pasaremos en la ruta el id asociado al mismo, y antes de cargar el componente asociado, haciendo uso de un resolver vamos a hacer una petición get a un api para traernos la información del usuario, de tal manera que cuando la página esté cargada la información se va a encontrar disponible.

En primer lugar vamos a crear un servicio que implemente la interface genérica resolve<T>.
Esta interface tiene el método resolver() que tenemos que sobreescribir.

En nuestro ejemplo, el método va a recibir un párametro de tipo ActivatedRouteSnapshot que vamos a usar para recuperar los parámetros de la ruta (vamos a tener rutas del estilo users/1, users/2, etc.)

const userId = route.params['id'];

Una vez que obtenemos el id del usuario, llamaremos al api de jsonplaceholder estableciendo una url del siguiente estilo https://jsonplaceholder.typicode.com/users/1




El componente donde vamos a mostrar la información del usuario se llamará user.component.ts
Esta información la va a recibir el componente una vez que el usuario haga clic en el enlace del menú asociado al mismo, y será entoces cuando se llame al resolver que se encargará de recibir el parametro del usuario y realizar la petición httpGet como veremos más adelante.

En el OnInit del componente lo que hacemos es recuperar la información de la llamada que hemos realizado al Api de jsonplaceholder. Esta información la tenemos en el objeto de la clase ActivatedRoute que inyectamos en el contructor, en concreto dentro de la propiedad "data".



En las rutas de nuestra aplicación, que las vamos a definir en el módulo principal será donde indiquemos los resolver que queremos asignar a cada una de ellas. Al inicio de la entrada comentamos que "un resolver es un código que se ejecuta después de hacer click en un enlace, y antes de que se haya cargado el componente asociado a dicho enlace"... y el objetivo que queremos es que cuando hagamos clic en el usuario-1 se cargue la información del usuario-2, con el usuario 2 la información del 2, etc.  por lo que las rutas asociada al componente UserComponent (users/:id) vamos a resolverlas con el UserResolverService

const ROUTES: Routes = [
  { path: '', component: AppComponent },
  { path: 'home', component: AppComponent },
  {
    path: 'users/:id',
    component: UserComponent,
    resolve: {
      user: UserResolverService
    }

  }
];


 Como podemos observar  en el código anterior, las rutas de "users" se resuelven con el servicio que hemos creado anteriormente, el que se encarga de realizar una petición http y dejar los resutados en "user"

En la siguiente imagen vemos el resultado que se muestra en el navegador:


A modo de resumen, hemos creado un servicio que implementa la interfece Resolve, sobreescribiendo el método resolve() de la misma.
Este método se encarga de obtener el parámetro de la ruta sobre la que hemos hecho clic, y devuelve un observable resultado de una petición http al api especificado.
Hemos definido las rutas, especificando para las rutas de usuario un resolve que está asociado al servicio creado inicialmente, y finalmente en el componente hemos pintado en el template los datos que hemos recuperado antes de que se cargara el mismo.

El código fuente completo de este ejemplo se encuentra en
https://github.com/jorgetrigueros/angular-resolver-demo


martes, 17 de abril de 2018

Cómo desplegar una aplicación Angular en GitHub

Normalmente estamos acostumbrados a usar GitHub como repositorio de código fuente de nuestras aplicaciones ya sean angular o de cualquier otro tipo, y no necesarimente solo aplicaciones.

En la entrada de hoy vamos a ver cómo podemos desplegar una aplicación Angular en GitHub.

En primer lugar crearemos lógicamente la aplicación angular que llamaremos "demo-deploy-angular-app-github"
La aplicación que vamos a desplegar es una aplicación angular con un componente que  muestre un simple "Hola mundo". Lo que realmente nos interesa en la entrada de hoy es ver como podemos desplegar una aplicación y no tanto el contenido o la complejidad de la misma.

Posteriormente necesitamos crear el repositorio en GitHub. En mi caso he creado un repositorio con el mismo nombre de la aplicación, es decir, "demo-deploy-angular-app-github"

Una vez que el repositorio ha sido creado vinculamos nuestro proyecto a GitHub, como en cualquier otro proyecto:

> git remote add origin https://github.com/jorgetrigueros/demo-deploy-angular-app-github.git
 
 Para desplegar una aplicación Angular en GitHub necesitamos instalar angular-cli-ghpages de manera global, por lo que ejecutaramos en nuestra consola la siguiente instrucción:

> npm install -g angular-cli-ghpages

  Una vez que hemos instalado la librería tenemos que construir el proyecto, es decir, tenemos que ejecutar desde el angular-cli la siguiente instrucción:

> ng build --prod --base-href "https://jorgetrigueros.github.io/demo-deploy-angular-app-github/"

El flag --pro indica que queremos hacer una construcción para producción. El flag --base-href  modifica la etiqueta base (<base href="/">) del index.html con la url establecida.

Después de haberse ejecutado el comando, se nos debería haber creado una carpeta "dist" con una serie de ficheros bundle.js como se ve en la siguiente imagen:

Una vez que hemos creado el proyecto, fnalmente nos queda publicarlo, y esto podemos realizarlo con el siguiente comando de la librería angular-cli-ghpages:

> angular-cli-ghpages --no-silent

Podemos ver las opciones disponibles para ejecutar ese comando en la página web de la librería:

https://www.npmjs.com/package/angular-cli-ghpages

Si todo ha ido bien veremos un mensaje de despliegue correcto, y podemos verificarlo si accedemos a nuestra dirección https://<username>.github.io/<reponame>

En nuestro ejemplo podemos ver el resultado en la dirección https://jorgetrigueros.github.io/demo-deploy-angular-app-github












domingo, 8 de abril de 2018

ngx-model: Libraría para la gestión del estado en Angular

A la hora de pensar como gestionar el estado en nuestras aplicaciones Angular lo lógico es pensar en utilizar una librería como ngrx.

El problema de ngrx es que supone un cambio importante en el paradigma de programación tal como estamos acostumbrados. Sin embargo, resulta indudable que tener separado el estado de la aplicación como una única fuente de verdad absoluta a la que todos consultan y donde todos modifican tiene grandes ventajas.

Si queremos gestionar el estado de nuestras aplicaciones Angular sin tener que recurrir a la complejidad de la librería ngrx, podemos usar Angular Model Pattern, una librería en la que en palabras de su autor sirve para Gestión de estados sencilla con una API minimalista, flujo de datos unidireccional, compatibilidad con múltiples modelos y datos inmutables expuestos como RxJS Observable

Para usar esta librería tenemos dos opciones:

1. instalando la dependencia ngx-model: npm install ngx-model --save
2. Creando nosotros el servicio tal como se indica en la página de la librería, en el apartado  "Use as Pattern" (https://tomastrajan.github.io/angular-model-pattern-example#/getting-started)

En cualquiera de los dos casos lo que la librería hace es, una vez que especificamos los tipo de datos con el que vamos a trabajar, -haciendo uso de tipos genéricos- usar un tipo "especial" de Observable (BehaviorSubject), para proporcionar métodos para la consulta y modificación de los datos del modelo, y otros métodos como el clonado de objetos manteniendo la inmutabilidad de los mismos.

Para hacer uso de la lbrería tenemos que:

- Importar ModelFactory en nuestro servicio, inyectándolo como dependencia en el constructor y crear una instancia de modelo.


      // inject model factory with specified type
      constructor(private modelFactory: ModelFactory<Todo[]>)
 

En los componentes o servicios que se quiera consultar/modificar el estado tendriamos que inyectar el servicio que hemos creado y hacer uso de los métodos del mismo que son los que gestionan el estado de nuestro modelo.

Vamos a verlo en un ejemplo que nos va a permitir guardar, actualizar y eliminar datos como nombre, apellidos y fecha de nacimiento de nuestros contactos que queramos almacenar.

Como queremos gestionar el estado de nuestros contactos vamos a crear una interfaz que represente este modelo.

export interface Contacto {
id: string;
nombre: string;
apellidos: string;
fechaNacimiento: string;
}

Vamos a crear un servicio de modelo donde utilizar esta interfaz para instanciar el modelo de nuestras contactos e implementar los métodos que cambian y consultan los datos del modelo, es decir, métodos para inserción, actualizacióny borrado de elementos.

El código de nuestro servicio es el siguiente:

@Injectable()
export class ContactoService {
private model: Model<Contacto[]>;
contacto$: Observable<Contacto[]>;

constructor(private modelFactory: ModelFactory<Contacto[]>) {
// create model and pass initial data
this.model = this.modelFactory.create([]); 
// expose model data as named public property
this.contacto$ = this.model.data$;
}

.........
}


En nuestro servicio hemos creado el modelo como un array de contactos a través del modelFactory y asignado a un Observable del mismo tipo.

El servicio tiene dos metodos. Uno para crear "contactos" que lo que hace es a través de la función set de la clase establecer el valor del "nuevo" modelo, que será lo que ya teniamos concatenando el nuevo contacto:

addContacto(nombre: string, apellidos: string,  
fechaNacimiento: Date) {
const id = generateUid('-');
const contactos = this.model.get();
this.model.set(contactos.concat([{ id, nombre, apellidos,  
fechaNacimiento }]));
}

y otro método para eliminar un contacto existente por medio de su Id, (que habremos generado a traves del método generateUid tal como puede observarse en el código anterior)

removeContacto(id) {
const contactos = this.model.get();
const index = contactos.findIndex(x => x.id === id);
if (index !== -1) {
this.model.set([...contactos.slice(0, index), 
 ...contactos.slice(index + 1, contactos.length)]);
}
}

Para el método de eliminar, obtenemos los contactos usando el método get() del modelo (model.get()) para posteriormente buscar el indice del elemento cuyo id es el especificado.
Si lo encontramos, establecemos el nuevo modelo dejando "fuera" el elemento encontrado, es decir, eliminando en otras palabras. Esto lo hacemos usando la propedad de inmutabilidad por medio del spread operator de ES6 para no tener que modificar el array inicial.

Por otra parte vamos a crear tres componentes, uno para crear nuestros contactos, otro para listarlos y un tercero que será el detalle del ccontacto en sí mismo.

El componente que crea los contactos (ContactoNuevoComponent) tendrá el siguiente layout:

<h1 class="title">
Añadir un contacto</h1>
<div class="field">
<label class="label">Nombre</label>
<div class="control">
<input class="input" type="text" placeholder="Nombre" #nombre>
</div>
</div>
<div class="field">
<label class="label">Apellidos</label>
<div class="control">
<input class="input" type="text" placeholder="Apellidos" 
#apellidos>
</div>
</div>
<div class="field">
<label class="label">Fecha nacimiento</label>
<div class="control">
<input class="input" type="text" 
 placeholder="Fecha nacimiento" #fechanacimiento>
</div>
</div>
<div class="field is-grouped">
<div class="control">
<button class="button is-link" 
(click)="addContacto(nombre.value, apellidos.value, 
fechanacimiento.value)">Añadir contacto</button>
</div>
</div>

en cuanto al código, unicamente tendremos el método addContacto que recibe como parámetros el nombre, apellidos y fecha de nacimiento del contacto que queremos crear:

addContacto(nombre: string, apellidos: string
 fechaNacimiento: Date): void {
this._contacto.addContacto(nombre, apellidos, fechaNacimiento);
}

En este método llamamos al método addContacto del servicio _contacto (ContactoService) que tendremos que inyectar en el constructor

El componente que nos va a mostrar nuestros contactos (ContactoListComponent) no tiene mucha historia. Vamos a cargar los contactos de nuestro modelo haciendo uso del servicio de contactos y a pasar el propio contacto al componente detalle que recibe como parametro Input() el contacto.

<div class="card" *ngFor="let contacto of _contacto.contacto$ | async;">
<app-contacto-detail [contacto]="contacto"></app-contacto-detail>
</div>


el componente que muestra el detalle (ContactoDetailComponent) recibe como parametro Input() el propio contacto y lo muestra en el template:
export class ContactoDetailComponent implements OnInit {
@Input() contacto: Contacto;
constructor(public _contacto: ContactoService) { }

<div class="card-content">
<div class="content">
<p class="card-header-title">
<span class="title">{{contacto.nombre}} {{contacto.apellidos}}</span>
</p>
<span class="subtitle">{{contacto.fechaNacimiento}}</span>
</div>
</div>
<footer class="card-footer">
<a href="#" class="card-footer-item" (click)="eliminarContacto(contacto.id)">Eliminar</a>
</footer>


y el método para eliminar el contacto llamando al método removeContacto del servicio:

/**
* Eliminar un contacto
* @param id
*/
eliminarContacto(id) {
this._contacto.removeContacto(id);
}


El resultado se verá similar al de la siguiente imagen:



En esta entrada hemos visto como podemos gestionar el estado en angular haciendo uso de la librería ngx-model, que si bien no tiene toda la potencia que nos ofrece las librerías de redux para angular, tampoco tiene la misma complejidad y puede sernos útil en multiples situaciones.

El código fuente se encuentra disponible en https://github.com/jorgetrigueros/ngx-model










sábado, 3 de marzo de 2018

Modificar el titulo y los metatags de una página dinamicamente en Angular 4

En ocasiones necesitamos modificar de manera dinámica el título de nuestra página, o los metatags de la misma (las etiquetas meta o meta tags se usan normalmente para resumir el contenido de la página para buscadores y navegadores web. Es decir, describen la página para que pueda ser entendida por diferentes servicios web).

En la entrada de hoy vamos a ver como podemos hacer esto en Angular4. Para ello crearemos una aplicación con unas pocas rutas, y cambiaremos el título y las metatags asociadas a dicha página o componente.

Para empezar vamos a definir algunas rutas para que cuando se acceda a la ruta correspondiente y se cargue el componente definido se muestre el título y los metatags especificados en la propia ruta mediante el objeto "data".

A la propiedad data podemos asignar un objeto json con la estructura que queramos definir. En nuestro ejemplo el data va a contener unicamente el título que queremos mostrar, aunque lógicamente podriamos definir otras propiedades como una descripción, por ejemplo.

La definición por tanto de nuestras rutas (app.routes.ts), con 3 componentes (helloCOmponent, InfoComponent, y LoginComponent)  es el siguiente:




Como podemos observar, a cada ruta hemos asignado un  parámetro"data" con un título para cada ruta.

El objetivo es cambiar los metadata y el título en base a la ruta en la que nos encontremos, luego lo primero que tenemos que hacer es obtener la propiedad "data" de la ruta donde nos encontramos actualmente. Eso lo conseguimos recorriendo la coleccion "events" de router. Como en realidad sólo nos interesa los eventos de tipo ActivationEnd, y más concretamente el primero de ellos, haremos:

this.router.events
.filter(event => event instanceof ActivationEnd && 
event.snapshot.firstChild === null 
)

Todo esto lo vamos a poner dentro de un método que llamaremos getRouteData en un servicio que llamaremos MetatagService. Como sólo nos interesa el objeto "data" lo "mapeamos" con el operador map. El método tiene el siguiente aspecto:




Tanto el título como las metadata las podemos establecer haciendo uso de la librería platform-browser de Angular el cual es un paquete que contiene las características que hacen posible la ejecución de una aplicación angular en el navegador.

Para cambiar el título podemos establecer el objeto Title mediante el método setTitle de manera similar a:

this.title.setTitle = data.titulo;

Para cambiar las metadatas tenemos que definir un objeto de tipo MetaDefinition.
Este tipo es un objeto json al que le pasamos el nombre (name) de la etiqueta meta y el contenido (content) que queremos establecer:

En el siguiente ejemplo estaríamos definiendo el metatag "description" con el valor del título establecido en el parametro data.

const metaTag: MetaDefinition = {
name: 'description',
content: data.titulo
};

Para actualizar el contenido de las meta haremos uso del método update. En el siguiente ejemplo obtenemos la ruta activa y establecemos la metatag con el título del data obtenido:

this.metatagService.getRouteData()
.subscribe(
data => {
console.log(data);

this.title.setTitle = data.titulo;

const metaTag: MetaDefinition = {
name: 'description',
content: data.titulo
};

this.metatagService.updateMeta(metaTag);
}
);


El resultado una vez que accedemos a la ruta correspondiente es similar al que se muestra en el siguiente pantallazo:



El objetivo de este artículo era demostrar cómo es muy sencillo cambiar el título y las metatags de manera dinámica haciendo uso de los métodos del platform-browser de Angular.

Espero haberlo conseguido...

Código fuente: https://cambiar-titulo-y-metatags-dinamicamente.stackblitz.io




viernes, 26 de enero de 2018

Template Reference Variables

En una entrada anterior vimos brevemente lo que son las template reference variables o variable de referencia de plantillas en Angular.

Una variable de referencia de plantilla es una referencia a un elemento o directiva del DOM dentro de una plantilla.
Se declara utilizando el prefijo # o bien la etiqueta ref- as dentro del elemento html.

Un ejemplo típico es un elemento input text del cual podemos obtener su valor accediendo a la propiedad value.

En el siguiente ejemplo obtenemos un número de teléfono y mostramos un mensaje cuando se hace click en un botón.




En el ejemplo anterior "telefono" hace referencia a un HTMLElement por lo que podemos acceder a todas sus propiedades y métodos de cualquier HTMLElement (id, name, value, etc.)

Esto puede resultarnos útil en ocasiones en las que no necesitamos crear un formulario para obtener el valor de un único elemento, como en el ejemplo anterior.

Sin embargo, con las Template Reference Variables podemos ir "más lejos"y referenciar también a un componente.

Imaginemos por ejemplo que tenemos un componente como el HelloComponent que crea por defecto stackblitz al que le hemos añadido tres variables privadas (para el nombre, apellido y email), y un método público que unicamente devuelve el email.

import { Component, Input } from '@angular/core';

@Component({
selector: 'hello',
template: `<h1>Hello {{name}}!</h1>`,
styles: [`h1 { font-family: Lato; }`]
})
export class HelloComponent {
@Input() name: string;
private nombre: string= 'Jorge';
private apellido: string = 'Trigueros';
private mail: string ='jorge.trigueros@gmail.com';

public mostrarEmail(){
return this.mail;
}
}

Podríamos referenciar el componente como una variable de referencia de plantilla de igual manera que como hemos hecho anteriormente con el HTMLElement, es decir:

<hello #hola></hello>

y de esta forma acceder tanto a las propiedades como a los métodos de dicho componente, tanto si son declarados privados como públicos.

 Es decir, podemos hacer lo siguiente:

<hello #hola></hello>
{{hola.nombre}} {{hola.apellido}}
{{hola.mostrarEmail()}}

De esta forma estamos accediendo tanto a las variables privadas del componente como al método que muestra el email accediendo "a través" de la variable de referencia del componente!!!

El resultado es el que se muestra en la pantalla siguiente:




Si bien no es la mejor forma de acceder a las variables y propiedades de un componente, en ocasiones se trata de una forma rápida que nos puede resultar de utilidad en determinados casos.

Espero que esta entrada haya servido para conocer un poco mejor las Template Reference Variables.


viernes, 29 de diciembre de 2017

View Encapsulation y Shadow DOM en Angular

En la entrada de hoy vamos a ver que es el view encapsulation y el Shadow DOM y como usarlos en Angular. Ante de nada empezaremos por definir que son los web components.
Los web components son como widgets que podemos usar en una aplicación web. Imaginemos por ejemplo un portal de noticias, podriamos tener un web component para mostrar las últimas noticias, otro web component para realizar una encuesta de un tema de actualidad, y otro para mostrar los resultados de la quiniela de la última jornada, por ejemplo.

Con Web Components puedes hacer casi cualquier cosa que pueda ser hecha con HTML, CSS y JavaScript, y puede ser un componente portable que puede ser reutilizado fácilmente.
En resumen, los web component son pequeños bloques de código, de diferentes tecnologías (javascript, css, html) que están encapsulados de tal forma que cada uno está aislado respecto los demás.

Uno de los problemas sin embargo cuando usamos componentes en un portal es que los estilos de un componente no entren en conflicto con los estilos de otro. Para evitar esto, los web components utilizan un mecanismo que se llama Shadow DOM, que consiste en un DOM encapsulado que vive dentro del DOM principal.

Para lograr interoperabilidad o resolución de conflictos entre componentes se inventó lo que en Angular se conoce como View Encapsulation. 

Hay tres tipos de encapsulación de vistas en Angular:

- Emulated
- Native
- None


Vamos a ver cada uno de estos tipos mediante un sencillo ejemplo. Vamos a crear tres componentes. Cada uno de ellos tendrá un botón y a aplicaremos los distintos tipos de encapsulación a cada uno de los componentes para ver los resultados.

Los componentes que vamos a crear se llamarán button-close.component.ts, button-open.component.ts y button-print.component.ts


El primer tipo de encapsulacion que vamos a ver es "emulated" que es el que viene por defecto. Para ello lo especificamos en la anotación "component" mediante el metadata encapsulation (encapsulation: ViewEncapsulation.Emulated) del ButtonPrintComponent tal como se muestra a continuación:


@Component({
selector: 'button-print',
template: `Button print`,
styleUrls: [ './button-print.component.css' ],
encapsulation: ViewEncapsulation.Emulated
})


El resto de componentes lo vamos a poner igualmente con este tipo de encapsulación para ver el resultado, luego nos quedaría algo como esto:


@Component({
selector: 'button-close',
template: `....`,
styleUrls: [ './button-close.component.css' ],
encapsulation: ViewEncapsulation.Emulated
})

@Component({
selector: 'button-open',
template: `...`,
styleUrls: [ './button-open.component.css' ],
encapsulation: ViewEncapsulation.Emulated
})

Vamos a usar Bulma como framework css, y por cuestión de simplicidad vamos a incluir un cdn a bulma dentro del index.html (mala práctica, pero en este caso lo hago por simplificar la entrada)

El código fuente del componente ButtonPrintComponent es el que se muestra a continuación:




El resto de botones tendría un aspecto similar.

Ahora vamos a colocar a la clase "button" de cada uno de los componentes, dentro de su css particular de cada uno de ellos un borde de color distinto para cada uno de ellos.

Es decir, en el button-print.component.css creamos la clase:

.button.is-primary {
  border:2px solid red;
}

para la clase del botón open:

.button.is-primary {
  border:2px solid blue;
}

y para el botón close:

.button.is-primary {
  border:2px solid black;
}

El resultado es el que podemos observar en el siguiente pantallazo:



Como podemos observar los estilos de un botón no han entrado en conflicto con los de los otros botones.

Si nos fijamos en el inspector de clases, vemos que se ha creado una clase nghost-cx para cada uno de los componentes.



Lo de host es un modo de emulación que es lo que precisamente se conoce como shadow DOM

Como resuelve Angular que los estilos de un componente no entren en conflicto con los de los otros? Si nos volvemos a fijar en el inspector vemos que para cada uno de los componentes se ha creado una propiedad "_ngcontent-cx" y una clase asociada a la misma, es decir, una clase .button.is-primary[_ngcontent-c1], .button.is-primary[_ngcontent-c2] y .button.is-primary[_ngcontent-c3] que representa a cada uno de los componentes que hemos creado.
 
Lo vemos mejor en el siguiente pantallazo:




Las clases que hemos mencionado anteriormente (.button.is-primary[_ngcontent-c1],.....) Angular las crea y las mete dentro del <head>.
De nuevo lo podemos ver mejor en el siguiente pantallazo:




Si cambiamos el tipo de encapsulación a uno de los botones, por ejemplo al primero, al de "print" por native, vemos que cambia y a a coger el shadow DOM del browser.

Lo que hace Angular es que ha metido todos los estilos de ese botón en algo que se llama "shadow-root". Este shadow-root es como si se generara una capsula donde se meten todos los estilos, pero no sólo los estilos del botón "print", si no los de ese botón y los del resto de componentes.
Este tipo de encapsulación no es soportado por todos los navegadores por lo que antes de usarlo hay que tener en cuenta que navegadores soportan dicha encapsulación. (Por ejemplo, IE0-IE11 no soportan este tipo de encapsulación)

Por último si usamos la encapsulación "none" lo que pasa es que se genera un estilo "global". Para verlo, vamos a poner en el css del último componente (ButtonCloseComponent) un color de fondo rojo:

(button-close.component.css)

.button.is-primary {
  border:2px solid red;
  background-color: red;
}

 Esto lo que hace es crear un estilo global tal como puede observarse en la siguiente imagen:




Luego en este caso, al haberlo aplicado sobre el último componente, afectaría al resto de componentes pudiendo modificar su aspecto. Lógicamente este tipo de encapsulación es la menos recomendable, al menos en lo que se conoce como DOM components, (componentes sin lógica)  ya que se pueden generar conflictos con otros componentes.

En la entrada de hoy hemos visto los tipos de encapsulamiento que trae Angular y se ha visto un ejemplo de uso de cada uno de ellos y explicado las diferencias entre ellos.

El código fuente esta disponible en:

https://view-encapsulation-in-angular.stackblitz.io

Hasta la próxima entrada :-)