lunes, 2 de enero de 2017

Angular 2. Formularios. Entrega 7: Templates (6). Template reference variables.

1. Definición


La referencia a una variable de la plantilla es una referencia a un elemento del DOM o una directiva en un template. O sea se puede utilizar con elementos nativos del DOM y también con componentes de Angular.

Se pueden referenciar en cualquier lugar de la template actual. No se deben repetir los nombres de las variables en la misma template.

2. Creación

Veamos 2 ejemplos de creación de variables, una con el prefijo "#" y el otro con el prefijo canónico equivalente ref-. Veamos un ejemplo de ambos, el primero, la variable phone y la segunda con la variable fax. Hay que observar que estas variables se han creado en elementos tipo <input>
<!-- phone refers to the input element; pass its `value` to an event handler -->
<input #phone placeholder="phone number">
<button (click)="callPhone(phone.value)">Call</button>

<!-- fax refers to the input element; pass its `value` to an event handler -->
<input ref-fax placeholder="fax number">
<button (click)="callFax(fax.value)">Fax</button>

3. Caso especial de NgForm

Podemos asignar una variable a un formulario, pero con la salvedad que no es del tipo HTMLFormElement sino a una referencia a la directiva de Angular NgForm que le otorga de "superpoderes" como rastrear la validez de la entrada de datos del usuario. 

En este ejemplo podemos ver como asignar una variable al NgForm y no dejamos hacer un "submit" si el formulario no contiene datos correctos.
<form (ngSubmit)="onSubmit(theForm)" #theForm="ngForm">
  <button type="submit" [disabled]="!theForm.form.valid">Submit</button>
</form>


4. Targets y sources

Hasta ahora vinculábamos expresiones con componentes que la parte derecha del binding (por tanto son origenes o sources)

Ahora nos centraremos en los destinos o targets que son propiedades de directivas en la parte izquierda del binding. En este último caso las propiedades de la directiva se deben declarar como inputs o outputs.

ACLARACIONES:

1. El target de un binding es la parte izquierda de un =
2. El target de un binding es la propiedad o evento dentro de la notación de binding: [], () o [()]
3. El source de un binding está entre comillas (" ") o dentro de una interpolación ( {{}} ).
4. Tenemos acceso limitado a miembros de una directiva target. Solamente podemos hacer bindings a propiedades que seon explicitamente identificadas como inputs y outputs

Veamos ejemplos de sources (que van a la derecha del = y van entre colmillas, por tantyo no se declaran con input o output.:
<img [src]="iconUrl"/>
<button (click)="onSave()">Save</button>
Ahora veamos 2 targets (el primer hero se decara entre corchetes y es una property binding y el segundo entre paréntesis es un event binding:
<hero-detail [hero]="currentHero" (deleteRequest)="deleteHero($event)">
</hero-detail>

5. Targets: propiedades de entrada y salida (input/output)

En el componente, estas variables target serán declaradas de 2 formas:
1. O con las anotaciones (decaradores) @input@output,
@Input()  hero: Hero;
@Output() deleteRequest = new EventEmitter<Hero>();
2. o como miembros de los arrays inputsinputs
@Component({
  inputs: ['hero'],
  outputs: ['deleteRequest'],
})
INPUT: Cuando la propiedad recoge los datos desde una expresion del template binding
OUTPUT: Cuando muestran los productores de eventos, tales como objetos EventEmitter

Inputs and outputs



6. Alias de inputs y outputs

Se pueden utilizar alias de los targets (input y/o outputs), ya que los nombres puede no ser muy esclarecedores.

Sea el siguiene código del template
<div (myClick)="clickMessage=$event">click with myClick</div>
que en el componente se puede indicar dicho alias entre paréntesis y comilla simple
@Output('myClick') clicks = new EventEmitter<string>(); 
que también se podría expresar en la forma del array
@Directive({
  outputs: ['clicks:myClick']  // propertyName:alias
})



Angular 2. Formularios. Entrega 6: Templates (5). El papel del asterisco '*' en el template

1. El elemento <template>

El asterisco (*) aplicado antes del nombre de las directivas es mas fácil de identificar, sobre todo aquellas que modifican el layout HTML.

Pero con el elemento <template>, tenemos que cambiar el asterico (*) por los corchetes []

1.1. Con NgIf

Por tanto podemos indicar para el NgIf:
-Sin template y con asterisco

<hero-detail *ngIf="currentHero" [hero]="currentHero"></hero-detail>
y con template con corchete y sin asterisco

<template [ngIf]="currentHero">
  <hero-detail [hero]="currentHero"></hero-detail>
</template>

1.2. Con NgSwitch

Lo mismo con NgSwitch, podemos utilizar tanto el asterisco sin template como el template con corchete y sin asterisco. Pero observese que tienen que ir juntos y al final el NgSwitchDefault de cada grupo.

<span [ngSwitch]="toeChoice">

      <!-- with *NgSwitch -->
      <span *ngSwitchCase="'Eenie'">Eenie</span>
      <span *ngSwitchCase="'Meanie'">Meanie</span>
      <span *ngSwitchCase="'Miney'">Miney</span>
      <span *ngSwitchCase="'Moe'">Moe</span>
      <span *ngSwitchDefault>other</span>

      <!-- with <template> -->
      <template [ngSwitchCase]="'Eenie'"><span>Eenie</span></template>
      <template [ngSwitchCase]="'Meanie'"><span>Meanie</span></template>
      <template [ngSwitchCase]="'Miney'"><span>Miney</span></template>
      <template [ngSwitchCase]="'Moe'"><span>Moe</span></template>
      <template ngSwitchDefault><span>other</span></template>

    </span>

1.3. Con NgSwitch

Lo mismo podemos decir con el NgFor, primero con el asterisco:

<hero-detail template="ngFor let hero of heroes; trackBy:trackByHeroes" [hero]="hero"></hero-detail>
Y con template con corchetes (Ojo: Observese la transformación of ->[ngForOF] y trackBy: -> [ngForTrackBy] 
<template ngFor let-hero [ngForOf]="heroes" [ngForTrackBy]="trackByHeroes">
  <hero-detail [hero]="hero"></hero-detail>
</template>




viernes, 30 de diciembre de 2016

Angular 2. Formularios. Entrega 5: Templates (4). Directivas

Anteriormente en la versión 1 de Angular, había más de 70 directivas, además la comunidad contriubuyó con muchísimas más. Pero se cree que no hace falta tantas directivas. Veamos las más importantes:

1. NgClass vs class binding

Es la mejor opción para agregar o quitar clases CSS dinámicamente.

Veamos el primer ejemplo donde utilizamos class binding en vez de NgClass, para modificar una sola clase.

!-- toggle the "special" class on/off with a property -->
<div [class.special]="isSpecial">The class binding is special</div>
En cambio con NgClass, podemos asignar varia clases a la vez. Veamos la función setClasses() que da varios valores de clase:

setClasses() {
  let classes =  {
    saveable: this.canSave,      // true
    modified: !this.isUnchanged, // false
    special: this.isSpecial,     // true
  };
  return classes;
}
Y en el template las podemos utilizar con NgClass:
<div [ngClass]="setClasses()">This div is saveable and special</div>

2. NgStyle vs style binding

Al igual que antes, vemaos un ejemplo de style binding, donde solo podemos assignar un estilo a la vez
<div [style.font-size]="isSpecial ? 'x-large' : 'smaller'" >
  This div is x-large.
</div>
En cambio con NgStyle podemos asignar varios estilos a la vez, como en el ejemplo anterior, creamos en el componente, una variable de tipo estructura que tenga los siguientes atributos, font-style, font-weight y font-size, y mediante el operador ":" les asignamos valores mediante el operador ternario (condicion ? valor_si_se_cumple : valor_si_no_se_cumple)

setStyles() {
  let styles = {
    // CSS property names
    'font-style':  this.canSave      ? 'italic' : 'normal',  // italic
    'font-weight': !this.isUnchanged ? 'bold'   : 'normal',  // normal
    'font-size':   this.isSpecial    ? '24px'   : '8px',     // 24px
  };
  return styles;
}
Y ahora en el template asignamos varios estilos a la vez.

<div [ngStyle]="setStyles()">
  This div is italic, normal weight, and extra large (24px).
</div>

3. NgIf vs visibilidad con clases y estilos

Hasta ahora, las directivas NgClass, NgStyle, se trascribían [ngClass ] y [ngStile] en el template , pero la directiva NgIf se escribe *ngIf en el template, y no nos olvidemos del asterisco.

Si queremos quitar elementos (y sus hijos)del DOM, utilizaremos NgIf, pero si no los queremos quitar pero si ocultar (hacerlos invisibles) podemos aplicar una clase o estilo invisible.
<div *ngIf="currentHero">Hello, {{currentHero.firstName}}</div>
Aquí si no existe currentHero, no muestra nada. Veamos otro ejemplo:
<!-- because of the ngIf guard
    `nullHero.firstName` never has a chance to fail -->
<div *ngIf="nullHero">Hello, {{nullHero.firstName}}</div>

<!-- Hero Detail is not in the DOM because isActive is false-->
<hero-detail *ngIf="isActive"></hero-detail>
Pero codemos ocultarlo (y no eliminarlo del DOM con clases)
<!-- isSpecial is false -->
<div [class.hidden]="!isSpecial">Show with class</div>


4. NgSwitch (cuidado con los *)

Es similar a la anterior, pero cuidado con los asteriscos y corchetes, ya que se utilizan 3 subdirectivas

1. [ngSwitch]
2. *ngSwitchCase
3. *ngSwitchDefault

Veamos un ejemplo en el template:
<span [ngSwitch]="toeChoice">
  <span *ngSwitchCase="'Eenie'">Eenie</span>
  <span *ngSwitchCase="'Meanie'">Meanie</span>
  <span *ngSwitchCase="'Miney'">Miney</span>
  <span *ngSwitchCase="'Moe'">Moe</span>
  <span *ngSwitchDefault>other</span>
</span>

5.NgFor e índices. Problemas de rendimiento.

Sirve para hacer repeticones. Se puede aplicar a un simple div o a un componente:

div *ngFor="let hero of heroes">{{hero.fullName}}</div>
<hero-detail *ngFor="let hero of heroes" [hero]="hero"></hero-detail>
OJO: Con el let hero del for creamos una variable de entrada del template, que NO es lo mismo que una variable de referencia del template

Tenemos las siguientes variables dentro del bucle NgFor:
1. index: Contador desde 0 en el bucle
2. first: si es el primero
3. last: si es el último
4. even: si es par
5. odd: si es impar

Aquí mostramos el nombre del héroe y sumamos 1 para que nos muestre el contador desde 1 y no desde 0:
<div *ngFor="let hero of heroes; let i=index">{{i + 1}} - {{hero.fullName}}</div>
Pueden surgir problemas de rendimiento, normalmente en listas largas. Una inserción o remoción de un elemento, puede disparar manipulaciones del DOM en cascada.
Si pedimos otra vez la lista de heroes desde el servidor, borra la lista antigua y la sustituye por la nueva. En cambio, si le aportamos una función que le indica que un héroe es el mismo si tiene el mismo id, entonces, angular no borrara los cambios efectuados desde el último refresco.

Veamos la función
trackByHeroes(index: number, hero: Hero) { return hero.id; }
Y también como utilizarla en el template con la clausula trackBy:
<div *ngFor="let hero of heroes; trackBy:trackByHeroes">({{hero.id}}) {{hero.fullName}}</div>




Angular 2. Formularios. Entrega 4: Templates (3). Binding bidireccional. NgModel

Vamos a ver el dindig bidireccional con la nomenclatura banana box y también con NgModel.


1. Two-way binding con banana box


La nomenclatura banana box [( )] , combina los corchetes de binding de propiedades [ ], con  los paréntesis del binding de eventos ( ).

Para ello se deberá utilizar las anotaciones @Input() de la variable del componente para que sea accedible a un componente padre, y también la aontación @Output() para exportar el evento.

Vale la pena mirar las explicaciones de Angular2 pero sobre todo mirar el live example.

No me voy a extender mas en este tema, ya que es mejor utilizar NgModel.


2. Two-way binding con NgModel


NgModel es la manera mas aconsejada del 2-binding:
<input [(ngModel)]="currentHero.firstName">
Y no hay que estar atento al evento de cambio de valores, ya que NgModel lo actualiza inmediatamente, sin tener que preocuparnos por interceptar eventos.


Pero requiere que se importe FormsModule en el módulo.
import { NgModule } from '@angular/core';
import { BrowserModule }  from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';

@NgModule({
  imports: [
    BrowserModule,
    FormsModule
  ],
  declarations: [
    AppComponent
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }
Pero a veces puede ser interesante interceptar los eventos de cambio del modelo,por ejemplo para pasar a mayúsculas. Entonce quedaría el template anterior

<input
  [ngModel]="currentHero.firstName"
  (ngModelChange)="setUpperCaseFirstName($event)">




Angular 2. Formularios. Entrega 3: Templates (2). Binding de Atributos, clases, estilos y eventos

Para no set tan enfarragosos como en la entrada anterior, solo veremos los bindings de los atributos, clases, estilos y eventos.

1. Binding de Atributos


Como ya vimos, hacer attribute binding es una excepción. Es decir, es mejor cambiar las PROPIEDADES del DOM que los atributos HTML.

Solamente, deberemos utilizar attribute binding cuando NO EXISTA dicha propiedad del DOM disponible para ser cambiada.

Como ejemplos de estos atributos tenemos: ARIA, SVG, y "table span". Son atributos puros y no existen propiedades a las que se pueda hacer un bind.

Si queremos pues cambiar dichos atributos, no se pueden colocar directamente si no a través de prefijo att seguido de un punto (.) y el nombre del atributo. Veamos un ejemplo



<table border=1>
  <!--  expression calculates colspan=2 -->
  <tr><td [attr.colspan]="1 + 1">One-Two</td></tr>

  <!-- ERROR: There is no `colspan` property to set!
    <tr><td colspan="{{1 + 1}}">Three-Four</td></tr>
  -->

  <tr><td>Five</td><td>Six</td></tr>
</table>

Aquí observamos que el texto entre comentarios que daría un error ya que NO encuentra la propiedad del DOM colspan.

Otro ejemplo vemos como cambiatr atributos de ARIA.

<!-- create and set an aria attribute for assistive technology -->
<button [attr.aria-label]="actionName">{{actionName}} with Aria</button>

2. Binding de clases.


Podemos asignar clases de 2 maneras:

1. Asignando [class]="variable que guarda el nombre de la clase".
2. Asignando [class.nombreClase]="variable booleana que activa o desactiva"

En el primer caso asignamos un único valor de clase, resetando otros valores de clase asignados previamente, mientras que en el segundo caso, si la variable booleana toma el valor false, desasignamos dicha clase (y en caso contrario la asignamos).

Veamos el primer caso

<!-- reset/override all class names with a binding  -->
<div class="bad curly special"
     [class]="badCurly">Bad curly</div>
Para el segundo caso


<!-- toggle the "special" class on/off with a property -->
<div [class.special]="isSpecial">The class binding is special</div>

<!-- binding to `class.special` trumps the class attribute -->
<div class="special"
     [class.special]="!isSpecial">This one is not so special</div>
Para manejar múltiles clases al mismo tiempo es aconsejable utilizar la directiva NgClass.


3. Binding de estilos.


Se definen asignando  [style.style.property]="expresión que devuelve un string"
Veamos ejemplos:
<button [style.color] = "isSpecial ? 'red': 'green'">Red</button>
<button [style.background-color]="canSave ? 'cyan': 'grey'" >Save</button>
<button [style.font-size.em]="isSpecial ? 3 : 1" >Big</button>
<button [style.font-size.%]="!isSpecial ? 150 : 50" >Small</button>
Pero se prefiere la directiva NgStyle.



4. Binding de eventos


Esta parte es un poco mas complicada. Aquí lo que se pretende es avisar al componente que se ha producido un evento, y la respuesta será ejecutar una función del componente.

Se  pueden utilizar en principio 2 nomenclaturas:

1.  (evento)="funcion_del_Componente() o expresión"
2.  on-evento="funcion_del_Componente() o expresion"

Siendo evento, el nombre del evento del elemento del DOM principalmente, o de una directiva. En un botón sería "click".
Veamos los ejemplos:
<button (click)="onSave()">Save</button>
<button on-click="onSave()">On Save</button>

OJO: Angular mira primero si existe un evento con el mismo nombre de una directiva, y si lo encuentra le da prioridad. Se verá mas adelante.

4.1 $event 

En un binding de evento, Angular establece un manejador de eventos (event handler) para el evento específico.

Cuando salta el evento, se ejecuta una función del componente, pero la información relacionada con el evento se guarda en un objeto llamado $event.

Si el evento es un evento nativo del DOM, entonces $event será del tipo evento del DOM, peró si el evento es de una directiva, el objeto será del tipo que haya definido internamente la directiva.

Veamos un ejemplo del primer caso, donde se actualiza el firstname del Hero a medida que escribimos.
<input [value]="currentHero.firstName"
       (input)="currentHero.firstName=$event.target.value" >


4.2 Eventos personalizados con EventEmitter

Para crear un evento en un componente, este evento debe ser del tipo EventEmitter. A continuación hay que crear una función (que se hace referencia en el template) que contenga el método "emit" con un parámetro.

En el ejemplo creamos un objeto deleteRequest del tipo EventEmitter<Hero>. Y el <Hero> determina el tipo de parametro a pasar al método emit.


// This component make a request but it can't actually delete a hero.
deleteRequest = new EventEmitter<Hero>();

delete() {
  this.deleteRequest.emit(this.hero);
}
Y en el template, habrá un botón que activará la función delete con el evento click que "emitirá un objeto de tipo "Hero".

<button (click)="delete()">Delete</button>
Pero por otra parte, tal como se dice en la información de angular2, podria haber un componente padre que hace un bind al objeto deleteRequest de nuestro componente anterior y recoge el objeto tipo Hero que se ha emitido con el método emit (a traves de $event) para proponer su borrado.
<hero-detail (deleteRequest)="deleteHero($event)" [hero]="currentHero"></hero-detail>



jueves, 22 de diciembre de 2016

Angular 2. Formularios. Entrega 2: Templates (I). Interpolación, propiedades y atributos. Binding

Veamos que nos podemos encontrar en una plantilla

1. Sintaxix moustache {{}} o Interpolación

Se puede usar en

1 Texto fuera de las tags de HTML. Permitiendo evaluación de expresiones simples
<p>My current hero is {{currentHero.firstName}}</p>
<!-- "The sum of 1 + 1 is 2" -->
<p>The sum of 1 + 1 is {{1 + 1}}</p>

2. Para dar valores a atributos de ciertas tags de HTML
<img src="{{heroImageUrl}}" style="height:30px">


2. Expresiones y sentencias del Template

Hay que tener en cuenta que no nos podemos referir a ningún componente del global namespace. Por ejemplo, no se puede tener acceso a window ni document, ni a console.log ni Math.max. Solamente podemos acceder a métodos y atributos del componente asociado.


Dirección de los datos Sintaxis Tipo de Bind
Modelo -> Vista
(único sentido)
{{expression}}
[target] = "expression"
bind-target = "expression"
Interpolation,
Property,
Attribute,
Class,
Style
Vista -> Modelo
(único sentido)
(target) = "statement"
on-target = "statement"
Event
Modelo <-> Vista 
(doble sentido)
[(target)] = "expression"
bindon-target = "expression"
Doble sentido
Donde se sustituye "target" por una propiedad o evento del DOM. Como se ve, hay una doble nomenclatura par decir lo mismo: 

  • [propiedad] equivale bind-propiedad 
  • (propiedad) equivale on-propiedad
  • [(propiedadequivale bindon-propiedad
Pero vamos a distinguir entre:

  • Atributos (que se definen en el HTML). No varían nunca. En un control input tipo texto con un valor inicial (atributo value="A"). Si depués de cambiar el usario el valor a "B" y hacemos "input.getAttributte('value')", nos devuelve "B" y NO "A". En cambio el DOM si tiene el valor correcto de "B".
  • Propiedades del DOM (Document Object Model). Pueden variar. No es lo mismo el atributo disabled de un boton (se incluye disabled o no se incluye en el HTML) y otra cosa es la propiedad disabled="true" del DOM
Como hemos dicho antes, solo podemos parametrizar PROPIEDADES y EVENTOS del DOM, y muy raramente atributos HTML.
Pero las propiedades y eventos se pueden especificar en elementos, componentes y directivas.

Veamos algunos ejemplos:



Elemento
Ejemplo
Propiedad de elemento <img [src] = "heroImageUrl" >
Propiedad de componente <hero-detail [hero]="currentHero" ></hero-detail >
Propiedad de directiva <div [ngClass] = "{selected: isSelected}"></div>
Evento de elemento <button (click) = "onSave()">Save</button>
Evento de componente <hero-detail (deleteRequest)="deleteHero()"></hero-detail>
Evento de directiva <div (myClick)="clicked=$event">click me</div>
Evento y propiedad two way binding <input [(ngModel)]="heroName">
Atributo (EXCEPCION) <button [attr.aria-label]="help">help</button>
Propiedad de clase <div [class.special]="isSpecial">Special</div>
Propiedad de estilo <button [style.color] = "isSpecial ? 'red' : 'green'">


Observaciones:

1. Si OMITIMOS los CORCHETES, no se evalua la parte derecha de la igualdad y se supone esta sefunda parte un string, con lo que si hacemos <hero-detail hero="currentHero"></hero-detail> sin cochetes en hero, se asignará al objeto hero el struing "currentHero" y NO el objeto llamado currentHero.
2. Podemos omitir los corchetes cuando se verifican todas estas premisas a la vez:

  2.1 El "target" acepta un valor string
  2.2 El string es un valor fijo.
  2.3 El valor inicial nunca cambia

3.  Se puede usar tanto interpolación {{}}, como property binding [], pero se está planteando priorizar a property binding, ya que parece mas claro.

<p><img src="{{heroImageUrl}}"> is the <i>interpolated</i> image.</p>
<p><img [src]="heroImageUrl"> is the <i>property bound</i> image.</p>

<p><span>"{{title}}" is the <i>interpolated</i> title.</span></p>
<p>"<span [innerHTML]="title"></span>" is the <i>property bound</i> title.</p>
La interpolación (de atributos) es traducida a las correspondientes propiedades del DOM antes de renderizar la vista.

4. Seguridad: Tanto la interpolación como el property binding, rechazan ejecutar el código <script> escondido en strings. Así:

  evilTitle = 'Template <script>alert("evil never sleeps")</script>Syntax';
<p><span>"{{evilTitle}}" is the <i>interpolated</i> evil title.</span></p>

daría: 

"Template <script>alert("evil never sleeps")</script>Syntax" is the interpolated evil title

mientras:

<p>"<span [innerHTML]="evilTitle"></span>" is the <i>property bound</i> evil title.</p>
daría: 


"Template Syntax" is the property bound evil title