Angular router

El siguiente post es continuación del resumen de Angular 2

Hay varias formas de hacer un router en angular, una de ellas es la siguiente utilizando:

Para el ejemplo partimos de :

  • Un componente cualqiera como puede ser login situdado en /login/login.component el cual exportamos:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import { Component, NgZone } from '@angular/core';
    @Component({
    selector: 'my-login',
    templateUrl: 'login.component.html',
    styleUrls: ['login.component.scss'],
    })
    export class LoginComponent {
    constructor() {}
    }
  • Creamos un modulo app-routing app-routing.module.ts, el cual importa:

    • Los componentes que vamos a routear
    • NgModule
    • Routes y RouterModule
    1
    2
    3
    import { NgModule } from '@angular/core';
    import { Routes, RouterModule } from '@angular/router';
    import { LoginComponent } from './login/login.component';

    En el modulo creamos un array de rutas del objeto Routes en el cual se indica el path y el componente a dicho path:

    1
    2
    3
    4
    export const routes: Routes = [
    { path: 'login', component: LoginComponent },
    { path: 'register', component: RegisterComponent }
    ];

    En @NgModule se importan las rutas para el routerModule y se exportan una vez añadidas las rutas:

    1
    2
    3
    4
    @NgModule({
    imports: [ RouterModule.forRoot(routes) ],
    exports: [ RouterModule ],
    })

Por último exportamos AppRoutingModule y además todos los componentes los cuales serán incluidos en AppModule para ser declarados, de esta forma no tenemos que importar uno a uno en AppModule, si no todos los componentes añadidos en el array de AppRoutingModule.

1
2
export class AppRoutingModule {}
export const routedComponents = [ LoginComponent, RegisterComponent ];

En definitiva AppRoutingModule sería por ejemplo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { LoginComponent } from './login/login.component';
export const routes: Routes = [
{ path: 'login', component: LoginComponent },
{ path: 'register', component: RegisterComponent },
];
@NgModule({
imports: [ RouterModule.forRoot(routes) ],
exports: [ RouterModule ],
})
export class AppRoutingModule {}
export const routedComponents = [ LoginComponent, RegisterComponent ];

Y en app.module se importa import { AppRoutingModule, routedComponents } from './app-routing.module'; para añadir a imports AppRoutingModule y declarar en declarations routedComponents.

app.module.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpModule } from '@angular/http';
import { AppComponent } from './app.component';
import { AppRoutingModule, routedComponents } from './app-routing.module';
import { AuthModule } from './shared/auth.module';
@NgModule({
imports: [
BrowserModule,
AppRoutingModule, // Important add AppRoutingModule
HttpModule
],
declarations: [
AppComponent,
routedComponents // All components in AppRoutingModule
],
bootstrap: [AppComponent]
})
export class AppModule { }

HTTP angular

El siguiente post es continuación del resumen de Angular 2

En lo posts anteriores:

Cargábamos los datos localmente desde fichero.

Con el servicio http de angular obtendremos los datos a través internet.

Con HTTP podemos hacer peticiones GET, POST, PUT, DELETE las cuales las podemos hacer de dos formas distintas:

  • Mediante observables, como por ejemplo el método login:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';
@Injectable()
export class AuthService {
private backendUrl = process.env.BACKEND_URI;
constructor(private http: Http) { }
login(credentials: Credentials) {
// console.log('cre', credentials);
this.http.post(this.backendUrl + '/login', credentials)
.map((res) => res.json())
.subscribe(
// We're assuming the response will be an object
// with the JWT on an id_token key
(data) => localStorage.setItem('jwtToken', data.jwtToken),
(error) => console.log(error)
);
}

Como se observa importamos la librería @angular/http y la instanciamos en el constructor.
Tambíen necesitamos importar rxjs/add/operator/map para map en el observable.

  • Con Promesas
1
2
3
4
5
6
getHeroes() {
this.heroService.getHeroes()
.subscribe(
heroes => this.heroes = heroes,
error => this.errorMessage = <any>error);
}

Se ha de tener en cuenta que en nuestro modulo tenemos que añadir http como provider en ambos casos.

Para más info Http client angular

Servicios

El siguiente post es continuación del resumen de Angular 2

En el post anterior Modelo de datos y mocks cargábamos datos mocks importando directamente el archivo, esta no es la mejor forma debido a:

  • Necesitamos importar el archivo en cualquier archivo que necesite acceso a los datos.
  • No es sencillo cambiar el flujo de trabajo entre los datos reales y los mockeados.
  • La carga de datos la realizan mejor las clases de services.
Los servicios son utilizados para organizar y compartir código a través de toda la app, suelen organizarse:

example.component.ts (Pide datos al servicio) ->
example.service.ts (accede a los datos para pasarselos al componente) -> mocks.ts

Como por ejemplo:

  • example.service.ts

    1
    2
    3
    4
    5
    6
    import { CARPARTS } from './mocks';
    export class RacingDataService {
    getCarParts() {
    return CARPARTS;
    }
    }
  • example.component.ts

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import { RacingDataService } from './racing-data.service';
    ...
    export class CarPartsComponent {
    carParts: CarPart[];
    ngOnInit() {
    let racingDataService = new RacingDataService();
    this.carParts = racingDataService.getCarParts();
    }
    }

De esta formar tenemos los datos desacoplados, no tenemos que importar los datos cuando nos sea necesario, solo el servicio y este es el que nos devuelve los datos, de esta forma si tenemos que cambiar la fuente de datos solo lo cambiamos en un solo lugar.

Los servicios en Angular 2 pueden y es conveniente usar el patrón de inyección de dependencias

El inyector de dependencias se encarga de crear y enviar los datos que le pidamos:

Sabe si es necesario instanciar los datos para enviarlos o enviar los ya instanciados.

Los pasos para hacer inyectable a un servicio son los siguientes:

  1. Añadir el decorator @inyectable al servicio.
  2. Indicar nuestro servicio inyectable en providers de @NgModule.
  3. Inyectar la dependencía en el componente que sea necesario.
  4. Utilizar el servicio en el componente en ngOnInit().

Por ejemplo:

  • 1.Añadir el decorator @inyectable al servicio.
1
2
3
4
5
6
7
8
9
import { CARPARTS } from './mocks';
import { Injectable } from '@angular/core';
@Injectable()
export class RacingDataService {
getCarParts() {
return CARPARTS;
}
}
  • 2.Indicar nuestro servicio inyectable en providers de @NgModule.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    ...
    import { RacingDataService } from './racing-data.service';
    @NgModule({
    declarations: [ AppComponent ],
    imports: [ BrowserModule, FormsModule ]
    bootstrap: [ AppComponent ] ,
    // En providers indicamos todos nuestros servicios inyectables
    providers: [ RacingDataService ]
    })
    class AppModule { }
    ...
  • 3y4 Inyectar la dependencía en el componente que sea necesario y utilizar el servicio en el componente en ngOnInit().

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    ...
    import { RacingDataService } from './racing-data.service';
    @Component({ ... })
    export class CarPartsComponent {
    carParts: CarPart[];
    // Inyectar la dependencia en el componente
    constructor(private racingDataService: RacingDataService) { }
    // Utilizarla
    ngOnInit() {
    this.carParts = this.racingDataService.getCarParts();
    }
    }

De esta forma conseguimos que nuestro servicio:

  • Sea desacoplado
  • Escalable porque nuestras dependencias están ligadas a las clases
  • Testeable porque es sencillo mockear

Two-way Binding

El siguiente post es continuación del resumen de Angular 2

En los posts anteriores hemos visto:

JavaScript to HMTL

HTML to JavaScript

Two-way Binding es la unión de ambos para tener una comunicación bidireccional, es posible hacerlo de dos formas:

  • Utilizando los dos Bindings:

    En este ejemplo en el mismo input se muestra la cantidad (Binding de propiedades) y se añade la cantidad (Binding de eventos):

1
<input class="number" type="text" [value]="carPart.quantity" (input)="carPart.quantity = $event.target.value">
  • Utilizando la síntasis [ () ] (Banana in the box)

Para ello en la clase del componente se importa @angular/forms:

1
2
3
4
5
6
7
8
9
10
11
...
import { FormsModule } from '@angular/forms';
@NgModule({
declarations: [ AppComponent ],
imports: [ BrowserModule , FormsModule ],
bootstrap: [ AppComponent ],
providers: [ RacingDataService ],
})
class AppModule { }
...

Y en el input se utiliza la síntasis [ () ]:

1
<input class="number" type="text" [(ngModel)]="carPart.quantity" >

De esta forma es mucho más clara y precisa.

Event Binding - Binding de eventos (One-Way Binding)

El siguiente post es continuación del resumen de Angular 2

En el post anterior se explicaba el (Binding de propiedades) el cual consistía en la comunicación de las propiedades de las clases de los componentes (TypeScript o JavaScript) al DOM, es decir:

JavaScript to HMTL

En el caso de Event Binding, es lo contrario:

HTML to JavaScript

Por ejemplo añadimos una función en nuestro componente:

1
2
3
4
5
6
export class CarPartsComponent {
...
upQuantity() {
alert("You Called upQuantity");
}
...

Y este es llamado desde HTML, por ejemplo al hacer click:

1
<button (click)="upQuantity( carPart )" >Click to alert</button>

Además del click tenemos los siguientes eventos:

1
2
3
4
5
6
7
8
9
<div (mouseover)="call()">
<input (blur)="call()">
<input (focus)="call()">
<input type="text" (keydown)="call()">
<form (submit)="call()">

Como es de esperar es posible coger información desde el elemento HTML para luego utilizarla en nuestra clase JS, por ejemplo vamos a mostrar un alert de cada letra que el usuario introduce en un input:

  • Código HTML:
1
<input type="text" (keydown)="showKey($event)">
  • TypeScript o JavaScript
export class CarPartsComponent {
  ...
  showKey(event) {
    alert(event.keyCode);
  }

  ...

En el siguiente post tratara de Two-way Binging, es decir comuncación bidireccional entre el DOM y Js.

Property Binding - Binding de propiedades y clases CSS (One-way binding)

El siguiente post es continuación del resumen de Angular 2

El Binding de propiedades significa mostrar las propiedades de nuesto componente en el DOM de la web y que cambie dinamícamente según cambía la propiedad, One-way binding es debido a que la comunicación es desde los modelos de los componentes de nuestra app al DOM no del DOM a la aplicación.

Con [ ] decimos a Angular 2 que esa propiedad del DOM va unida a una propiedad de nuestro modelo como por ejemplo:

  • <img [ src ] =" carPart.image ">
  • <div [ hidden ] ="!user.isAdmin" >secret</div>
  • <button [ disabled ] ="isDisabled" >Purchase</button>
  • <img [ alt ] ="image.description">

Binding Class (CSS)

También es posible hacer binding de una clase CSS dependiendo del modelo.

Por ejemplo la clase CSS

1
2
3
.featured {
background: #57595D;
}

Es posible añadirla dependiendo del modelo, en este caso de ejemplo cardPart.featured devuelve true o false y si es true se añade la clase .featured al elemento, está es la única síntasis posible:

<div [class.featured]="carPart.featured" >

Hay que tener en cuenta, que de esta forma cada elemento tiene su scope, el código generado sería el siguiente:

<div _ngcontent-opf-2 >

1
2
3
.featured[_ngcontent-opf-2] {
background: #57595D;
}

En definitiva:

  • El binding de propiedades permite hacer binding entre las propiedades del componente y el DOM.
  • Cualquier actualización del valor de la propiedad actualizara el DOM, pero no viceversa, una actualización del DOM no actualiza el modelo (One-way binding).
  • El binding de clases CSS nos permite especificar estilos a un elemento del DOM si la propiedad del componente es true, tienen su propio scope del estilo que es dado.

Modelos y uso de mocks

El siguiente post es continuación del resumen de Angular 2

Modelos en angular 2

Básicamente los modelos en angular 2 son clases como por ejemplo:

1
2
3
4
5
6
7
export class Carpart {
id: number;
name: string;
description: string;
inStock: number;
price: number;
}

De esta forma en la clase de nuestro componente podemos crear arrays de la clase Cardpart, importando la clase.

1
2
3
4
5
6
7
8
9
10
11
12
import { Component } from '@angular/core';
import { CarPart } from './car-part';
export class CarPartsComponent {
carParts: CarPart[] = [{
"id": 1,
"name": "Super Tires",
"description": "These tires are the very best",
"inStock": 5,
"price": 4.99
}, { ... }, { ... }]
...

Uso de mocks en angular 2

Una de las formas posibles para comenzar a desarrollar nuestra aplicación sin disponer aún los datos que nos va a suministrar la API es mediante Mocking (mockear datos).
Una forma simple es creando nuestro array de datos JSON o de clases que vayamos a utilizar y asignarlo a las propiedades de los componentes que nos sea necesario.

Como por ejemplo:

1
2
3
4
5
6
7
8
9
import { CarPart } from './car-part';
export const CARPARTS: CarPart[] = [{
"id": 1,
"name": "Super Tires",
"description": "These tires are the very best",
"inStock": 5,
"price": 4.99
}, { ... }, { ... }];

Y ahora en nuestro componente mediante la función ngOnInit() damos el valor a la propiedad correspondiente, en este ejemplo a la propiedad cardParts se le asigna el valor CARDPARTS el cual es el array de cardparts que hemos importado.

1
2
3
4
5
6
7
8
9
10
11
12
13
import { Component } from '@angular/core';
import { CarPart } from './car-part';
import { CARPARTS } from './mocks';
...
})
export class CarPartsComponent {
carParts: CarPart[] ;
ngOnInit() {
this.carParts = CARPARTS;
}
...

De esta forma podemos comenzar a desarrollar nuestra app con datos de prueba mocks ;).

Organización de código

El siguiente post es continuación del resumen de Angular 2

Organización de los componentes en Angular 2

Como vulgarmente se dice:

divide o vencéras

Partiendo del siguiente ejemplo en el que tenemos AppComponent en app/main.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import { NgModule, Component } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
@Component({
selector: 'my-app',
template: `<h1> {{title}} </h1>
<h2>{{carPart.name}}<h2>
<p>{{carPart.description}}<p>
<p>{{carPart.inStock}} in Stock<p>`
})
class AppComponent {
title = 'Ultra Racing';
carPart = {
"id": 1,
"name": "Super Tires",
"description": "These tires are the very best",
"inStock": 5
};
}
@NgModule ({
declarations: [ AppComponent ]
})
class AppModule { }
platformBrowserDynamic()
.bootstrapModule(AppModule);

Movemos toda la parte del componente AppComponent a un fichero aparte como por ejemplo app.component.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: `<h1>{{title}}</h1>`
})
// Importante, exportar para utilizar el componente
export class AppComponent {
title = 'Ultra Racing';
carParts = [...];
totalCarParts() { ... };
}

Es importante, exportar para utilizar el componente export class..

En conclusión ahora disponeos de dos archivos:

  • main.ts Que importa el componente:
1
2
3
4
5
6
7
8
9
// Importante hacer el import
import { AppComponent } from './app.component';
@NgModule({
declarations: [ AppComponent ],
imports: [ BrowserModule ],
bootstrap: [ AppComponent ]
})
class AppModule { }
  • Y el propio componente en app.component.ts
1
2
3
4
5
6
7
8
9
10
11
12
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: `<h1>{{title}}</h1>`
})
export class AppComponent {
title = 'Ultra Racing';
carParts = [...];
totalCarParts() { ... };
}
Mover el código HTML CSS

Ahora procedemos a separar el código del template (html) y añadir los estilos,

Como por ejemplo en el componente anterior:

1
2
3
4
5
6
7
8
9
10
11
12
13
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: 'app/app.component.html',
styleUrls:['app/car-parts.component.css']
})
export class AppComponent {
title = 'Ultra Racing';
carParts = [...];
totalCarParts() { ... };
}

Como podemos observar el código HTML se encuentra en un archivo aparte app.component.html :

1
2
<p>There is a example ;).</p>
<ul>...</ul>

y el css en otro app.component.css :

1
2
3
4
5
6
7
.description {
color: #444;
font-size: small;
}
.price {
font-weight: bold;
}

De esta forma, nuestro componente se encuentra dividido en tres partes cada una en un archivo:

  • La lógica del componente en app.component.ts el cual se exporta para incluirlo en @module en main.ts.
  • El template con el html en app. component.html
  • Estilos del componente en app.component.css

Angular 2 - Estructura de directivas, Pipes y Métodos

Directivas

El siguiente post es continuación del resumen de Angular 2

Una directiva de Angular es la forma de añadir comportamiento dinámico a HTML mediante la etiqueta o selector que creamos.

Existen tres tipos diferentes de directivas:

Podemos verlas en el fragmento de código:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
...
@Component({
// El selector y templatte sería el primer tipo de directiva
selector: 'my-app',
template: `<h1>{{title}}</h1>
<ul>
<li *ngFor="let carPart of carParts">
<h2>{{carPart.name}}<h2>
<p>{{carPart.description}}<p>
<p *ngIf="carPart.inStock > 0">{{carPart.inStock}} in Stock</p>
<p *ngIf="carPart.inStock === 0">Out of Stock</p>
</li>
</ul>`
// En el propio template observamos las directivas de estructura *ngFor y *ngIf
})
class AppComponent {
...

Pipes y Métodos

Un pipe coge un dato de entrada y lo transforma a la salida deseada, como por ejemplo:

Métodos (Methods) como podemos imaginar es añadir métodos en la clase de nuestro componente para luego ser utilizada:

1
2
3
4
5
6
7
8
9
10
11
12
...
@Component({
selector: 'my-app',
template: `<h1>{{title}}</h1>`
})
class AppComponent {
exampleMethod() {
return 10;
}
}

Angular 2 - Primer componente Angular 2

El siguiente post es continuación del resumen de Angular 2

Para comenzar añadir en index.html el primer componente, llamado en este caso <my-app></my-app>

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html>
<head>
<!-- All the Angular 2 libraries needed -->
</head>
<body>
<my-app>Loading App ...</my-app>
</body>
</html>

Para cargar Js hay varias opciones, una de ellas es utilizar la librería SystemJS que se utiliza en el curso de codeschool:

El siguiente código añade el código de app/main.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html>
<head>
<!-- All the Angular 2 libraries needed -->
<script>
System.import('app')
.catch(function(err){ console.error(err); });
</script>
</head>
<body>
<my-app>Loading App ...</my-app>
</body>
</html>

También una buena opción es con webpack como explican al detalle en la web oficial de angular.io.

En app/main.ts añadimos nuestro primer componente:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import { NgModule, Component } from '@angular/core';
//Las siguientes dependencias son para renderizar en el navegador
import { BrowserModule } from '@angular/platform-browser';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
// Our component decorator code goes here:
@Component({
//la etiqueta añadida anteriormente <my-app></my-app>
selector: 'my-app',
// El contendio que se carga en nuestro componente
template: `<h1> {{title}} </h1>
<h2>{{carPart.name}}<h2>
<p>{{carPart.description}}<p>
<p>{{carPart.inStock}} in Stock<p>`
})
class AppComponent {
//Definimos las propiedades que usamos en el template con {{ }}
title = 'Ultra Racing';
carPart = {
"id": 1,
"name": "Super Tires",
"description": "These tires are the very best",
"inStock": 5
};
}
// El nuevo componente lo debemos declarar en el decorador @NgModucle
@NgModule ({
declarations: [ AppComponent ]
})
class AppModule { }
// Bootrasp app (Arrancar la aplicación)
platformBrowserDynamic()
.bootstrapModule(AppModule);

Tanto @Component como @NgModule son decorators, una feauture de typescript, explicado de forma breve digamos que “extiende” la funcionalidad o comportamiento de la clase a un objeto: