lunes, 2 de enero de 2017

Angular 2. Formularios. Entrega 8: Templates (7). Template expression operators. Operador pipe

Las template expressions utilizan un subconjunto de la sintaxis de JavaScript con unos pocos operadores para escenarios específicos. Solamente explicaramos 2 operadores el pipe y el safe navegation. En esta entrada solo hablaremos del pipe.

1. El operador pipe (|). Introducción

Al igual que se hace en unix, el operador pipe realiza transformaciones ya sea para filtrar, ordenar, convertir a mayúscula etc.

Veamos las utilidades que tiene de serie:

1. Convertir a mayúscula {{title | uppercase}}
2. Convertir a minúscula {{title | lowercase}}
3. Encadenamiento de conversiones {{title | uppercase | lowercase}}
4. Dar formato como fecha {{currentHero?.birthdate | date}}
5. Aplicar parámetros {{currentHero?.birthdate | date:'longDate'}}
6. Transformar a json (depuración) {{currentHero | json}}
7. Dar formato de moneda {{importe | currency}}
8. Dar formato de porcentaje {{porcentajePerdidas | percent}}
9. Dar formato a un número {{potencia | number}}
10. Devolver el último valor de un obsevable o promesa  {{promesa$ | async}}
11. Internacionalización {{numero | i18nPlural:mapping}} ver mas abajo.
12. Recorte de string o array   {{miString | slice:comienzo[:fin]}}

NOTA: Los pipes de fecha (date) y moneda (currency) necesitan la ECMAScript Internationalization API. Safary y otros navegadores antiguos no la sportan. Se puede añadir un polyfill  en el template.

<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=Intl.~locale.en"></script>

2. Parámetros de algunos pipes predefinidos

Algunos pipes predefinitos admiten porcentajes:
1. number[:digitInfo]   teniendo digitInfo este formato;
     {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}
2. percent[:digitInfo] teniendo digitInfo el mismo formato anterior
3. currency[:currencyCode[:symbolDisplay[:digitInfo]]] donde
  currencyCode cumple el ISO 4217 (USD para el dolar y EUR para el euro)
  digitInfo es igual que en apartados anteriorresal anterior y
  symbolDisplay es booleano e indica si se muestra el símbolo de la moneda o no

3. date[:format] donde el formato puede ser:
  3.1 'medium' (yMMMdjms) , 'short' (yMdjm): Para indicar fecha y hora
  3.2 'fullDate' (yMMMMEEEEd), 'longDate' (yMMMMd), 'mediumDate'(yMMMd),
         'shortDate'(yMd): para inidicar fechas
  3.3 'mediumTime'(jms) y 'shortTime'(jm): Para indicar horas

  Además se contemplan los siguientes símbolos:
  3.4 : G (era), y (year), M (month), d (day), E (weekday),
  3.5:  j (hour), h (hour12), H (hour24), m (minute), s (second)
  3.6: z (timezone), Z (timezone), a (timezone)

Veamos un ejemplo con fechas:
<p>Today is {{today | date}}</p
<p>Or if you prefer, {{today | date:'fullDate'}}</p>
<p>The time is {{today | date:'jmZ'}}</p>

4. i18nPlural:mapping debiendo cumplir mapping el formato ICU.
5. islice:comienzo[:fin]

3. Parámetros con expresiones

Pero estos parámetros a parte de poder admintir constantes (numericas de string etc), tambien adminten template expressions.

Veamos un ejemplo donde cambiamos la variable miFormato de shortDate a fullDate.
En el template:
 <p>The hero's birthday is {{ birthday | date:miFormato }}</p>
  <button (click)="toggleFormat()">Toggle Format</button>
En el componente:
export class HeroBirthday2Component {
  birthday = new Date(1988, 3, 15); // April 15, 1988
  toggle = true; // start with true == shortDate

  get miFormato()   { return this.toggle ? 'shortDate' : 'fullDate'; }
  toggleFormat() { this.toggle = !this.toggle; }
}


4. Creación de pipes a medida

Para ello tenemos que:
1. Utilizar la anotación @Pipe ({name: 'miPipe'}) a la que pasamos el nombre miPipe 
2. Realizar la importación en el import de  Pipe y PipeTransform
3. Debemos incluir nuestro pipe en declarations del AppModule.
4. La clase pipe debe implementar el método transform el interface PipeTransform que admite valores de entrada y otros valores opcionales y devuelve el valor transformado.

Veamos un ejemplo que damos de nombre al pipe exponentialStrength, al que le pasamos 2 números, y el primer número lo elevamos al segundo, El segundo número es opcional.
Vemos el pipe (app/exponentual.strength.pipe.ts)
import { Pipe, PipeTransform } from '@angular/core';
/*
 * Raise the value exponentially
 * Takes an exponent argument that defaults to 1.
 * Usage:
 *   value | exponentialStrength:exponent
 * Example:
 *   {{ 2 |  exponentialStrength:10}}
 *   formats to: 1024
*/
@Pipe({name: 'exponentialStrength'})
export class ExponentialStrengthPipe implements PipeTransform {
  transform(value: number, exponent: string): number {
    let exp = parseFloat(exponent);
    return Math.pow(value, isNaN(exp) ? 1 : exp);
  }
}
Veamos el template (2 es el primer parámetro y 10 el segundo)
<h2>Power Booster</h2>
<p>Super power boost: {{2 | exponentialStrength: 10}}</p>
También se pueden utilizar template expressions en el template tal como vimos anteriormente.

Hay que tener cuidado con las actualizaciones del contenido de un array. Angular tiene una referencia a un array al que podemos aplicarle un pipe. Si añadimos elementos al array, la referencia al array no cambia, por tanto, no actualiza la pantalla. Si queremos que cambie, se tiene que cambiar la referencia a un nuevo array. (En C, Java,.. y la mayoria de lenguajes, se guarda la referencia o posicion de memoria del array u objeto, sin importarnos en primera instancia el contenido del mismo)


5. Pipes puros e impuros

Un pipe impuro permite detectar el cambio del contenido de un array u objeto. Pero tiene la desventaja que consume muchos mas recursos del sistema para ir testeando el componente.

Para inidicar que un pipe es impuro, solamente hay que indicar pure: false en fla anotación
@Pipe({
  name: 'flyingHeroesImpure',
  pure: false
})

6. El pipe AsincPipe

Es un pipe que es impuro que se aplica a una promesa o observable, devolviendo los valores que se emiten. Es stateful, ya que nos reporta los valores del observable a medida que nos llegan.

Veamos un ejemplo de un compopnente

  1. import { Component } from '@angular/core';
  2. import { Observable } from 'rxjs/Observable';
  3. import './rxjs-extensions';
  4. @Component({
  5. selector: 'hero-message',
  6. template: `
  7. <h2>Async Hero Message and AsyncPipe</h2>
  8. <p>Message: {{ message$ | async }}</p>
  9. <button (click)="resend()">Resend</button>`,
  10. })
  11. export class HeroAsyncMessageComponent {
  12. message$: Observable<string>;
  13. private messages = [
  14. 'You are my hero!',
  15. 'You are the best hero!',
  16. 'Will you be my hero?'
  17. ];
  18. constructor() { this.resend(); }
  19. resend() {
  20. this.message$ = Observable.interval(500) // cada 500 mseg emite un entero
  21. // comenzando por 0 e incrementando en 1
  22. // o sea 0,1,2,3,4,5,6....
  23. .map(i => this.messages[i]) //asignamos el mensaje i-esimo
  24. .take(this.messages.length); // paramos pasada la longitud del vector
  25. }
  26. }

7. Filter & Sort

Angular ya no suministra pipes para filtrar y ordenar ya que bajan el rendimiento. Para ello cada cual debe proporcionar estas herramientas desde el componente.


1 comentario :

  1. Si necesitas una herramienta de traduccion colaborativa para traducir sitios o aplicativos en Angular 2, yo recomiendo que usar POEditor.
    Esa herramienta en linea soporte ficheiros .xmb y .xtb

    ResponderEliminar