
Angular 2
Jesús Rodríguez
Consultor en IdeaBlade
- Intro
- @Decoradores
- Componentes
- Entrada y salidas
- Directivas
- Servicios
- Tuberías
- NgModule y bootstrap
- Http
- Router
- angular-cli

Agenda
- NodeJs
- Editor de textos con soporte de TypeScript
-
Webstorm https://www.jetbrains.com/webstorm
- Git

Prerrequisitos
- Framework del lado del cliente.
- Impulsado por Google.
- Angular 1 tuvo una gran cuota de mercado. Angular 2 ha sido reescrito desde cero.
- Promociona TypeScript como lenguaje recomendado.

Angular 2

Soporte en navegadores

Funciones que añaden metadatos a una clase, función o propiedad.

@Decoradores
class UserService {
getUser(id: number) {
return { name: 'Fake', email: "fake@email.com" }
}
getUsers() {
return { // muchos usuarios }
}
}Queremos hacer log de todas las funciones llamadas:

@Decoradores
class UserService {
getUser(id: number) {
// Algunas lineas para hacer logging
return { name: 'Fake', email: "fake@email.com" }
}
getUsers() {
// Algunas lineas para hacer logging
return { // muchos usuarios }
}
}Podemos crear un decorator y marcar las funciones a loguear.

@Decoradores
export function log(target: Function, key: string, value: any) {
return {
value: function (...args: any[]) {
// lógica del logging
return originalResult;
}
}
}Aplicamos nuestro nuevo decorador donde lo necesitemos:

@Decoradores
class UserService {
@log
getUser(id: number) {
return { name: 'Fake', email: "fake@email.com" }
}
@log
getUsers() {
return { // muchos usuarios }
}
}O también a nivel de clase:

@Decoradores
@loggable
class UserService {
getUser(id: number) {
return { name: 'Fake', email: "fake@email.com" }
}
getUsers() {
return { // muchos usuarios }
}
}Más información: https://goo.gl/WYt26m
Plunker: https://goo.gl/hfMECS

Hola mundo
@Component({
selector: '...'
template: `...`,
styles: `...`,
animations: '...'
// muchas más propiedades
})Las aplicaciones Angular están hechas con componentes:
export class MyComponent { }Vamos a ver cómo es un "Hola mundo":

Hola mundo
import { Component } from '@angular/core';
@Component({
selector: 'gr-app',
template: `<div>Hola mundo</div>`
})
export class AppComponent { }Esto más que mundo es Golden Race, ¿no?

Hola... Golden Race?
import { Component } from '@angular/core';
@Component({
selector: 'gr-app',
template: `<div>Hola {{name}}</div>`,
})
export class AppComponent {
name = 'Golden Race';
}Plunker: https://goo.gl/VsjxBt
Entrada (Input):

Entrada y salida
<gr-component home="Manchester"></gr-component>
<gr-component [away]="awayTeam"></gr-component>
<img src="http://someimage.com"></img>
<img [src]="teamLogo"></img>Salida (Output):
<div (click)="selectTeam()">I like this team</div>
<div (keyup.enter)="confirm($event)">Confirm selection</div>Entrada y salida (two-way databinding):
<gr-component [(team)]="team"></gr-component>Corchetes, no corchetes, paréntesis, los dos a la vez... ¿Qué es lo próximo? ¿Interrogaciones? ¿Exclamaciones?

Entrada y salida
Un ejemplo real de Angular 1:
<gr-component select="team.city(current)"></gr-component><gr-component date="{{data}}"></gr-component><gr-component date="date"></gr-component>
<gr-component date="'date'"></gr-component>
Entrada y salida
¿Cómo soluciona Angular 2 este problema?
Introducir texto como entrada:
<gr-component home="Milan"></gr-component>Introducir una variable como entrada:
<gr-component [home]="home"></gr-component>Recibir un evento del componente:
<gr-component (select)="apply()"></gr-component>
Entrada y salida
¿Y de lado del componente?
import {
Component,
Input,
EventEmitter,
Output
} from '@angular/core';
Component({...})
export class BetComponent {
@Input() home: string;
@Input() away: string;
@Output() select = new EventEmitter<string>();
}
Entrada y salida
¿Y en el template?
template: `
<h2>Place your bet:</h2>
<p (click)="onSelect(home)">home: {{home}}</p>
<p (click)="onSelect(away)">away: {{away}}</p>
`onSelect(team) { this.select.emit(team); }Emitimos el evento:

Entrada y salida
Ahora usamos el componente:
<gr-bet [home]="home" away="Liverpool" (select)="onSelect($event)"></gr-bet>export class AppComponent {
home = 'Málaga';
selected: string;
onSelect(event) {
this.selected = event;
}
}Plunker: https://goo.gl/cYNwXo
Two-way databinding: https://goo.gl/IUghvM

Directivas
Los componentes son un tipo de directiva, ¿para qué se usan las directivas?
import { Directive, ElementRef, Input } from '@angular/core';
@Directive({ selector: '[grHighlight]' })
export class HighlightDirective {
constructor(el: ElementRef) {
el.nativeElement.style.backgroundColor = ‘yellow’;
}
}<p myHighlight>Your team won!</p>Plunker: https://goo.gl/uxIoD0

Directivas incluidas
*ngIf - Ocultar o mostrar un elemento:
@Component({
selector: 'gr-app',
template: `
<p>You won't see anything below this</p>
<div *ngIf="show">
<h2>You can't see me :(</h2>
</div>
`,
})
export class AppComponent {
show = false;
}Plunker: https://goo.gl/qQezv9

Directivas incluidas
*ngFor - repetir un elemento múltiples veces:
@Component({
selector: 'gr-app',
template: `
<ul>
<li *ngFor="let team of teams">
{{team.name}} from {{team.country}}
</li>
</ul>
`,
})
export class AppComponent {
teams = [
{ name: 'Madrid', country: 'Spain' },
{ name: 'Liverpool', country: 'England' },
{ name: 'Milan', country: 'Italy' }
];
}Plunker: https://goo.gl/qmjlzD

Directivas incluidas
¿Qué hay detrás del asterisco?
template: `
<ul>
<template ngFor let-team [ngForOf]="teams">
<li>
{{team.name}} from {{team.country}}
</li>
</template>
</ul>
`template: `
<template [ngIf]="show">
<h2>You can't see me :(</h2>
</template>
`
Servicios
Los servicios nos ayudan a encapsular la lógica de negocio.
export class TeamService {
constructor(private http: Http) { }
getTeams() {
return this.http.get('api/teams')
.map((res) => res.json().data);
}
getTeam(id: number) {
return this.http.get(`api/teams/${id}`)
.map((res) => res.json().data);
}
}
Servicios
Para consumir un servicio, lo inyectamos donde lo necesitemos:
export class AppComponent implements OnInit {
teams: any;
bestTeam: any;
constructor(private teamService: TeamService) { }
ngOnInit() {
this.teamService.getTeams()
.subscribe(res => this.teams = res; );
this.teamService.getTeam(12)
.subscribe(res => this.bestTeam = res; );
}
}
Servicios
Hablando de servicios... ¿qué es y cómo funciona la inyección de dependencias?
export class AppComponent {
constructor(public foo: Foo, bar: Bar, public baz: Baz) {}
}Es como una caja donde guardamos las instancias de nuestros servicios.

Servicios
Pero... si al transpilar se pierde el tipado, ¿cómo sabe Angular qué tiene que inyectar?
import { Injectable } from '@angular/core';
@Injectable()
export class TeamService (
constructor(private http: Http) { }
...
}__metadata('design:paramtypes',
[(typeof (_a = typeof http_1.Http !== 'undefined'
&& http_1.Http) === 'function'
&& _a) || Object]
)Plunker: https://goo.gl/ZYLRZi

Tuberías
Los pipes (o tuberías) nos permiten transformar una entrada de la forma que necesitemos:
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({ name: 'capitalize'})
export class CapitalizePipe implements PipeTransform {
transform(value: string) {
return value[0].toUpperCase() + value.substring(1);
}
}@Component({
selector: 'gr-app',
template: `{{ name | capitalize }}`
})
export class AppComponent {
name = 'golden race';
}
Tuberías
También son capaces de aceptar parámetros:
@Pipe({ name: 'capitalize'})
export class CapitalizePipe implements PipeTransform {
transform(value: string, all: boolean) {
if (all) {
return value.split(' ').map(word => {
return word[0].toUpperCase() + word.substring(1);
}).join(' ');
} else {
return value[0].toUpperCase() + value.substring(1);
}
}
}template: `{{ name | capitalize: true }}`Plunker: https://goo.gl/Y7fpWy

NgModule y bootstrap
Las distintas piezas de Angular 2 se organizan dentro de módulos:
@NgModule({
imports: [ BrowserModule, HttpModule ],
declarations: [ AppComponent, BetComponent, CapitalizePipe ],
providers: [ TeamService ],
bootstrap: [ AppComponent ]
})
export AppModule {}Puedes tener todo en un solo módulo, o dividirlo entre las distintas secciones de tu aplicación. Incluso puedes cargar un módulo sólo cuando sea necesario (lazy loading).

NgModule y bootstrap
Los módulos de secciones (features) declararán y exportarán sus componentes y opcionalmente algún módulo:
@NgModule({
imports: [ CommonModule ],
declarations: [ TeamComponent, TeamDetailComponent ],
exports: [ TeamComponent ]
})
export class TeamModule { }Plunker: https://goo.gl/SFTqpx

NgModule y bootstrap
Uno de los módulos será el que usemos para lanzar la aplicación:
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {AppModule} from './app.module';
platformBrowserDynamic().bootstrapModule(AppModule)Y usaremos el selector del componente de bootstrap en nuestro index.html:
<gr-app>loading...</gr-app>Plunker: https://goo.gl/Y7fpWy (el mismo de tuberías)

Http
Con Http nos podemos comunicar con el servidor.
import { HttpModule } from '@angular/http';
@NgModule({
imports: [ ... , HttpModule, ... ],
...
})Importamos:
Inyectamos y usamos:
export class UserService {
constructor(private http: Http) { }
getUsers() {
return this.http.get('api/users');
}
}
Http
Podemos transformar el resultado usando rxjs:
export class TeamService {
constructor(private http: Http) { }
getTeams() {
return this.http.get('api/teams')
.map((res) => res.json().data);
}
}Y si no queremos trabajar con observables, siempre podemos transformar la respuesta a una promesa:
export class TeamService {
constructor(private http: Http) { }
getTeam(id: number) {
return this.http.get(`api/teams/${id}`)
.map((res) => res.json().data)
.toPromise();
}
}
Http - Promesas vs Observables
- Las promesas son un patrón que trata de eliminar el callback-hell proporcionando un API más limpio.
- Los observables van un paso más allá y transforman streams de datos reactivos por donde fluyen los cambios (rxjs). Es un modelo de trabajo más declarativo.
- HttpModule y otros módulos de Ng2 usan Observables como API base y lo exponen como tal. Es el modo recomendado.
Plunker: https://goo.gl/TaixD5

Router
El router de Angular 2 es un router de componentes. Eso quiere decir que mapea cada ruta a un componente a mostrar:
const routes: Routes = [
{ path: '', redirectTo: '/teams', pathMatch: 'full' },
{ path: 'teams', component: TeamsComponent },
{ path: 'teams/:id', component: TeamDetailComponent },
{ path: 'bet', component: BetComponent }
];Y finalmente exporta un módulo con las rutas:
@NgModule({
imports: [ RouterModule.forRoot(routes) ],
exports: [ RouterModule ]
})
export class AppRoutingModule {}
Router
Podemos navegar a otras rutas de dos maneras:
constructor(private router: Router) { }
gotoTeams() {
this.router.navigate(['/teams']);
}<a routerLink="/teams">Teams</a>Desde nuestro template:
O desde el componente:

Router
Si desde "teams" queremos navegar al detalle de uno de los equipos, podemos crear una ruta relativa:
<a [routerLink]="[team.id]">{{ team.name }}</a>constructor(private route: ActivatedRoute) { }
ngOnInit() {
this.id = this.route.snapshot.params['id'];
}Desde TeamDetailComponent podemos acceder al id así:

Router
Por muchas rutas que tengamos, seguimos necesitando un sitio donde alojar esas páginas:
<router-outlet></router-outlet>router-outlet será reemplazado con el template del componente enrutado.
Plunker: https://goo.gl/Idk7Cm

angular-cli
angular-cli es una de las opciones que tenemos para crear nuestras aplicaciones:
$ npm install -g angular-cliLa gran ventaja de angular-cli es que todas las herramientas no son visibles al usuario, lo que facilita a un equipo el trabajo y permite al cli actualizarse sin conflictos.
$ ng new goldenrace
$ ng serve
$ ng test
Angular 2
By Jesus Rodriguez
Angular 2
- 464