miércoles, 14 de diciembre de 2016

Angular 2. Entrega 11. Composición de componentes

Los componentes se pueden combinar en Angular 2, formando un árbol. Micael Gallego, ha hecho un gráfico típico de como se pueden combinar los componentes en una aplicación. De esta forma se puede tener un equipo de personas trabajando en componentes distintos.

Vamos a suponer que tenemos un componente App que hace uso del componente Header. Para ello, vamos a suponer que el Header la vamos a delegar en un componente.

1. Declaración del componente en app.module.ts

Para ello en app.component.html se introduce al principio este código
<header></header>
que nos va a servir para referenciar a un componente con el "selector" igual a "header".

Ahora creamos dicho componente header.component.ts que tenga su templateUrl apuntando a header.component.html

Y por supuesto, no olvidar declarar dicho componente en app.module.ts, tanta en la parte de import como en la de declarations.


import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';

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

@NgModule({
  declarations: [AppComponent, HeaderComponent],
  imports: [BrowserModule, FormsModule, HttpModule],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }


Ya que si no lo indicamos, el compilador no lo va a encontrar.


2. Comunicación del componente padre al hijo

1. El padre puede especificar propiedades en el componente hijo como si fuera un elemento nativo HTML. Por ejemplo creamos atributo1 . Veamos como queda app.component.html cuando llamamos a la etiqueta <header>

<header [atributo1]='variable1'></header>
<p> Otro contenido</p>

En este caso asignamos atributo1 a variable1 que tendrá que estar definida en el componente padre app.component.ts
...
export class AppComponent {
  variable1="Cabecera";
..
}

Pero en header.component.ts tenemos que definir atributo1 con la anotación @Input() , la cual se tendrá que definir en el import

import {Component, Input} from '@angular/core'
...
export class HeaderComponent {
   @Input()
  private atributo1: string;
  ..
}

Y ahora en header.component.html tiene que haber una referencia "moustache" a atributo1

<h1> {{atributo1}} <h1>

En efecto, y le inyectamos el valor "Cabecera" que contiene variable1 del componente padre al hijo.

3. Generación de eventos en el hijo que se atienden en el padre

Para ello vamos a crear un evento llamado "ocultar". En el componete padre creamos un método "ocultarContenido" que se ejecutará cuando se active dicho evento. Y lo que hace es hacer de "chivato" en la consola que ha recibido el evento. Veamos el componente padre app.component.ts

...
export class AppComponent {
  ocultarContenido (oculto: boolean) {
    console.log("He recibido el evanto. Ahora Ocultar=" + oculto)
  }
  ...
}

y el app.component.html tenemos que destacar el evento "ocultar" entre paréntesis, y la acción que realiza, pero en este caso el parámetro del método es $event !

<header (ocultar) = 'ocultarConteniodo($event)'></header>
<p>Otro contenido</p>

En cuanto al hijo  header.component.ts tenemos que declarar en el import además EventEmitter,y Output. También tenemos que usar la anotación @Output() a la variable ocultar que para inidicar que el evento se transmite al padre. dicha variable es del tipo EventEmitter de booleanos. Y definiremos el evento que será "click" del boton (del html) asociándole codigo que la función clickBoton(), en el cual aparece el evento oculto lanzando el método emit con un parámetropara que el padre lo reciba

import {Component, Output, EventEmitter } from '@angular/core';
...
export class HeaderComponent {
  @Output()
  oculto = new EventEmitter<boolean>()
  
  visible = true; // Suponemos inicialmente que mostramos información.

  clickBoton () {
    this.visible= !this.visible;
    this.oculto.emit(this.visible)
  }
 ...
}

Donde el click del boton definido en el header.component.html

<h1 *ngIf=visible">Lo que queramos mostrar</h1>
<button (click)='clickBoton()'>Ocultar/Mostrar<button>


4. ¿Cuando crear un nuevo componente?


  • Cuando la lógica o el template se hagan demasiado largos o complicados
  • Cuando se puedan reutilizar en varios contextos.

martes, 13 de diciembre de 2016

Angular 2. Entrega 10. Formularios

En angular podemos controlar los formularios:

1. Vincular un control de un formulario a un atributo del componente (data binding), para ello utilizamos [(ngModel)] con "banana box". Aquí el valor se actualiza en tiempo real.

<input type="text" [(ngModel)] = myName>

2. Acceso a los controles desde el código para leer y modificar su estado. Para ello podemos asignarle un nombre al control (ojo este nombre no es un id), y desde ottas partes del template podemos referirnos a él.

<input #fruitInput type"text">
<button (click)="update(fruitInput.value); fruitInput.value=''">
  update Fruit
</button>


3. Mecanismos avanzados de validación con ngControl. Se verá mas adelante.


Angular 2. Entrega 9. Templates y directivas

Las templates son parte de los ficheros html (o sea no es javascript o ts). Segun Micael, permiten definir la vista en función del componente, permitiendo:

1. Visualización condicional
2. Repetetición de elementos.
3. Estilos.
4. Formularios

1. Visualización condicional, directiva ngIf

ngIf incluye el código entre "<>" si es verdadero. Puede ser asignado a un atributo que sea booleano, y siempre la cosa a comparar tiene que estar entre comillas dobles "".

En este caso si el atributo "visible" es verdadero, incluirá el código :

<p *ngIf="visible">Texto a mostrar</p>

En este otro caso, se puede usar una expresión,

<p *ngIf="num == 3">Texto a mostrar</p>


2. Repetición de elementos , directiva ngFor

Dado un array, definido normalmente en un component. Por ejemplo en app.component.ts añadimos la variable frutas

frutas = [{nombre:'manzana', color:'amarillo'},
          {nombre:'pera', color:'verde'},
          {nombre:'granada', color:'palido'},
          {nombre:'mandarina', color:'naranja'}];

ahora dentro del fichero app.component.html indicamos

<div *ngFor= "let fruta of frutas"> {{fruta.nombre}} </div>

3.Restricciones
No se pueden incluir 2 directivas estructurales (de tipo *) en el mismo elemento. Es decir son directivas que modifican el DOM. Esto estaría MAL

<li *ngFor "let fruta of frutas" * ngIf ="fruta.color=='naranja'"> {{fruta.nombre}}
</li>

Hay que usar la versión de las directivas sin el "sugar sintax" (*), en si versión extendidad con el elemento template (que no aparece en el DOM)

<template ngFor let-elem [ngForOf]="frutas">
  <li *ngIf="fruta.color==='naranja'"> {{fruta.nombre}}</li>
</template>

3. Estilos

Los estilos en CSS son globales, cosa que provoca colisiones.

Si queremos que los CSS sean GLOBALES, tenemos 2 opciones:
1. Indicarlo en el index.html.
2. Usar src/styles.css. El cual, angular-cli lo incluye de forma automática en index.html

Si queremos hacerlos  LOCALES se puede hacer:
1. Dar valor a las variables styles o stileUrls. Pero no se pueden utilizar los 2 a la vez !
2. También se puede tener de forma local  en el template, metiendo un elemento <style>

Veamos como se modican los estilos de un elemento:

1. Activar la clase de un elemento a un atributo de tipo string. En este caso tenemos un componente que tiene una variable llamada className que puede valer "blue".

<h1 [class]="className">Hola!"</h1>

2. Activar una clase concreta a un atributo boolean. Siendo

<h1 [class.blue]="blueActive">Hola!"</h1>

3. Asociar la clase de un elemento a un atributo de tipo mapa de string a boolean. Utilizamos la directiva ngClass que permite asociar mapas a las clases de un elemento.

<h1 [ngClass.blue]="pClasses">Hola!"</h1>

Donde en el componente se ha definido un mapa

pClasses: { 
  "red" : false,
  "bold": true
}
y un método para cambiar los valores:

changeParagraph () {
  this.pClasses.bold= true;
}


4. Asociar un estilo concreto de un elemento a un atributo.

<p [style.backgroudColor]="pColor">Hola"</p>

y se pueden meter unidades numéricas en estos casos:

<p [style.fontSize.em]="pSizeEm">Hola"</p>
<p [style.fontSize.%]="pSizePerc">Hola"</p>

y se pueden usar mapas de propiedad utilizando la directiva ngStile :

<p [ngStyle]="getStyles()">Hola"</p>

getStyles() {
  return {
    'font-style': this.canSave? 'italic':'normal,
    'font-weight:!this.isUnchanged? 'bold':'normal',
    'font-size':this.isSpacial? '24px':'8px',
  }



Angular 2. Entrega 8. Depuración del código

Nuestro amigo Micael nos comenta que hay una herramienta de depuracion muy buena llanada augury pero, en principio vamos a trabajar con chrome.

Para ello si hacemos F12 sobre el navegador Chrome, vemos la vista de desarrollador, y vamos a la pestaña sources, y dentro hay una carpeta llamada webpack (que como vimos es nuestro gestor de proyecto), y en el se pueden ver nuestro componentes. Se pueden poner puntos de ruptura en el código, en nuestro caso hemos hecho click sobre el número de la línea, en este caso la 15.

Cuando le damos al botón, automáticamente el código se para en la línea 15 y nos muestra el valor de la variable myName  que es "John Travolta"





Angular 2. Entrega 7. Data Binding con ngModel

con el "Data Binding", hacemos que la vinculación sea bidireccional (tanto javascript con html)

1. ngModel

 Para ello utilizamos con la nomenclatura "banana" [(ngModel)]

Veamos el nuevo fichero app.component.html

<h2>Títol:</h2>
<h1> {{title}} </h1>
<img [src]="imgUrl" />

<input type="text [(ngModel)]="myName">
mi nombre es {{myName}}

<button (click) = "setMyName('John Travolta')" > Hola John </button>

Si ahora miramos el navegador, vemos que a medidas que modificamos el contenido de la caja de texto, se actualiza también un poco mas abajo.

Angular 2. Entrega 6. Ficheros index.ts y src/main.ts

Hay 2 ficheros que hay que tener en cuenta:
El primero es index.ts, que exporta todos los ficheros "ts" de una carpeta. Con ello se pretende crear un fichero que aglutine a otros ficheros. referenciando en un único fichero todos los elementos que se exportan. Sus ventajas son:

1. Simplificar la importación desde otros ficheros
2. Desacoplar los tipos de ficheros en el que se declaran

export * from './app.component';
export * from './app.module';


el segundo es src/main.ts, que para importar AppModule, hace uso del fichero anterior y utiliza solo la referencia a la carpeta donde está dicho fichero, es decir './app' .

import './polyfills.ts';

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { environment } from './environments/environment';
import { AppModule } from './app/';

if (environment.production) {
  enableProdMode();
}

platformBrowserDynamic().bootstrapModule(AppModule);





Angular 2. Entrega 5. El módulo de la aplicación

El módulo es un elemento de la aplicación de angular que define las dependencias que se utilizan en la aplicación. En nuestro caso tenemos el fichero app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';

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

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Por una parte nos dice los elementos básicos de angular 2 que utilizamos en este caso los 4 primeros "imports", y a continuación nos indica otro import con nuestro componente anterior (AppComponent).

Observar que el 2º import hace referencia a NgModule, ya que estamos en un módulo,

A continuación viene la anotación @NgModule para indicar que esto es un módulo. Y además contiene estas declaraciones:
1. declarations: Indica los componentes a usar
2. imports: enumera los componentes internos de angular a utilizar (excepto NgModule). Por defecto importa también FormsModule ya que lo mas normal es que mas adelante le añadamos un formulario. Como dice Micael, "es un elementa muy básico".
Por otra parte, angular2 puede vivir en BackEnd o en Browser. En este caso por defecto nos suministra el valor BrowserModule.
3. providers: Parece ser que se tendrán que definir los servicios (otro elemento mas de angular)
4. bootstrap: Componentes utilizados al arrancar la aplicación, o sea cuales son los elementos principales. En nuestro caso solo está de momento AppComponent.

Al final se exporta la clase AppModule que según Micael, es una clase "de mentira", ya que nunca va a tener código.