Extensiones VSC: https://marketplace.visualstudio.com/items?itemName=Angular.ng-template https://marketplace.visualstudio.com/items?itemName=johnpapa.Angular2
Tags & explicación:
ng new tour-of-heroes-es
Para hacer un verdadero "hello world" eliminamos los modulos que no necesitamos.
ng serve
ng g component listado
installing component
create src/app/listado/listado.component.css
create src/app/listado/listado.component.html
create src/app/listado/listado.component.spec.ts
create src/app/listado/listado.component.ts
update src/app/app.module.ts
bootstrap: [AppComponent] => bootstrap: [ListadoComponentComponent]
Modificamos el Index para que levante el tag de listado. Modificamos los datos de listado para que muestre html bindeado.
para modifcar a los heroes utilizamos en app.module.ts
import { FormsModule } from '@angular/forms';
y [(ngModel)]
El prefijo (*)
del ngFor
es una parte importante de esta sintaxis. Indica que el elemento <li>
y sus hijos constituyen una plantilla "maestra".
La directiva ngFor
itera sobre la matriz de héroes del componente y procesa una instancia de esta plantilla para cada héroe en esa matriz.
La parte del héroe let
de la expresión identifica a héroe como la variable de entrada de plantilla, que contiene el elemento heredero actual para cada iteración. Puede hacer referencia a esta variable dentro de la plantilla para acceder a las propiedades del héroe actual.
Ahora que tenemos un listado de hereos lo vamos a tener que elegir para poder ver los detalles del mismo ( master -> detail).
Lo que vamos a hacer es ejecutar una funcion click sobre cada uno... para que angular reconzca que vamos a ejecutar una funcion click tenemos que agregar las parentesis por ejemplo:
<li (click)="seleccionar(heroe)">Tormenta</li>
.
Ahora que tenemos el objeto del héroe seleccionado, lo que vamos a hacer es mostrarlo arriba en la parte del template que teníamos cambiando la variable a heroeSeleccionado
.
El problema que vamos a tener es que nuestra consola nos va a dar un error por "null o undefined" debido a que al iniciar nuestra web app no tiene valor nuestro heroeSeleccionado
.
Para eso vamos a utilizar *ngIf
y de esta forma vamos a mostrar los datos sólo si tiene valor y evitamos el error.
Por último lo que vamos a hacer es agregar una class a nuestro listado solamente cuando este seleccionado, para eso vamos a escribir en nuestro tag li
<li *ngFor="let heroe of HEROES" (click)="seleccionar(heroe) [class.selected]="heroe === heroeSeleccionado"></li>
ahora que ya tenemos el listado completo, deberiamos tener un componente aparte aparte para el detalle...
El nombre de clase de componente debe ser escrito en CamelCase y terminan en la palabra "component".
La clase de componentes de detalle héroe es HeroeDetalleComponent
.
El nombre del archivo del componente debe ser deletreado en el caso del guión medio, cada palabra separada por guiones, y terminar en .component.ts
. La clase HeroeDetalleComponent
se encuentra en el archivo heroe-detalle.component.ts
.
Para generar nuestro componente y sus archivos vamos a ejecutar:
ng g c heroe-detalle
installing component
create src/app/heroe-detalle/heroe-detalle.component.css
create src/app/heroe-detalle/heroe-detalle.component.html
create src/app/heroe-detalle/heroe-detalle.component.spec.ts
create src/app/heroe-detalle/heroe-detalle.component.ts
update src/app/app.module.ts
Ahora vamos a sacar de nuestro listado la parte del detalle y la vamos a agregar en nuestro nuevo componente.
Ahora vamos a necesitar la clase que creamos de heroe que tenemos en el archivo listado.component.ts
, como la vamos a utilizar en varios archivos, vamos a generar un archivo para nuestra clase heroe y poder importarla donde la necesitemos.
Una vez creada simplemente tenemos que importarla
import { Heroe } from '../heroe'
En nuestro componente principal (listado) vamos a agregar nuestro componente detalle... esto lo haremos de la siguiente forma:
<app-heroe-detalle [heroe]="heroeSeleccionado"></app-heroe-detalle>
El tag es el generado por el componente, lo que está entre corchetes es el parámetro (nombre del input) que va a recibir el el componente y lo que está adentro de las comillas es la variable que le vamos a pasar.
Lo que nos queda hacer, es que nuestro componente detalle pueda obtener parametros al ser inicializado, para eso vamos a importar desde nuestro componente a Input y luego indicar que el "Input" que va a recibir es heroe
@Input() heroe: Heroe;
Vamos a crear un servicio para Heroes para poder consumir desde cualquier componente Para ello vamos a ejecutar:
ng g service heroe
installing service
create src/app/heroe.service.spec.ts
create src/app/heroe.service.ts
WARNING Service is generated but not provided, it must be provided to be used
Vamos a ver que tenemos un WARNING que nos avisa que no lo agrego a providers... Los servicios los podemos agregar en providers (no es obligatorio) dentro del app.module... en nuestro caso lo vamos a realizar y el @NgModule
va a quedar asi
@NgModule({
declarations: [
AppComponent,
ListadoComponent,
HeroeDetalleComponent
],
imports: [
BrowserModule,
FormsModule
],
providers: [HeroeService],
bootstrap: [ListadoComponent]
Ahora que tenemos creado el servicio vamos a trabajar sobre él...
Vamos a ver que importa Injectable
, quien nos va a dejar poder utilizar y mantenerlo como un singleton sobre toda nuestra app...
Antes de arrancar con el servicio vamos a crear un mockup de datos para poder consumirlo.
Para ello, vamos a crear una carpeta llamada mockups y dentro el archivo llamado mock-heroes.ts
import { Heroe } from '../heroe';
export const HEROES: Heroe[] = [
{id: 11, nombre: 'Mr. Nice'},
{id: 12, nombre: 'Narco'},
{id: 13, nombre: 'Bombasto'},
{id: 14, nombre: 'Celeritas'},
{id: 15, nombre: 'Magneta'},
{id: 16, nombre: 'RubberMan'},
{id: 17, nombre: 'Dynama'},
{id: 18, nombre: 'Dr IQ'},
{id: 19, nombre: 'Magma'},
{id: 20, nombre: 'Tornado'}
];
Ahora que tenemos nuestro injectable (ver archivo heroe.service.ts
) vamos a llamarlo donde lo necesitamos para no harcodear datos en los componentes
ver => listado.component.ts
constructor(private heroeService: HeroeService) { }
Esto lo que va a hacer es traer a nuestro servicio y asignarlo a una variable (recordemos que es un singleton por lo tanto lo vamos a tener vivo mientras este vivo el tag)
Luego lo podemos llamar en el ngOnInit =>
que es cuando se inicia el componente (lo dejé comentado) o tambien lo podemos llamar a través de una función para cuando tengamos la necesidad de traer los datos (en el botón getHeroes()
).
Tambien se puede ver que traemos a los Heroes en distintas formas, para eso podés ver el archivo heroe.service.ts
, que vamos a tener llamadas con promise y con delay para "simular" a una ida y vuelta del servidor
Vamos a dividir nuestro componente listado ahora
app.component
=> será nuestra vista principallistado.component
=> será nuestro listado maestroheroes-detalle.component
=> será nuestro detail
Para ello vamos a modificar el index.html
<app-listado>Loading...</app-listado>
por
<heroes-app>
<img src="https://cdn-images-1.medium.com/max/800/1*1LufzuvnUabrvKL9yp6LMQ.gif"></img>
</heroes-app>
En nuestra app.module.ts
vamos a cambiar el componente en el cual arranca o sea el bootstrap a AppComponent
y en AppComponent
vamos a cambiar el selector a heroes-app
.
De esta forma ya tenemos nuestra base en app.component
Ahora lo que vamos a hacer es crear el router, para ello vamos a hacer una serie de pasos:
- Agregar en el head (index.html) el siguiente tag
<base href="/">
- Importar el
routerModule
en elapp.module
- Declarar
RouterModule
en nuestro import (ngModule) - Agregamos el
forRoot
a nuestro archivo para configurar las rutas
imports: [
BrowserModule,
FormsModule,
RouterModule.forRoot([
{ path: 'heroes', component: ListadoComponent }
])
],
Con estos pasos vamos a tener la primera parte del routeo.
Pero no queda todo ahora.. recuerden que nuestro app.component solo tiene un Hello World... vamos a cambiar eso por un menu, vamos a modificar nuestro html por este:
<h1>{{title}}</h1>
<a routerLink="/heroes">Heroes</a>
<router-outlet></router-outlet>
Ahora que vemos como funciona el router, vamos a cambiar a heroes-detalle
para que obtenga el parametro de la url en una ruta distinta y no dependa del componente listado.
Para eso primero vamos a agregar la nueva ruta:
{ path: 'detalle/:id', component: HeroeDetalleComponent }
Borramos el tag del componente en listado.component
Y a nuestro HeroeDetalleComponent
vamos a agregar los siguientes imports
import { Component, Input, OnInit } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import { Location } from '@angular/common';
import { HeroService } from './hero.service';
Ademas vamos a importar switchMap
para poder manejar los parametros como observables
import 'rxjs/add/operator/switchMap';
y agregamos en nuestro ngOnInit
la llamada a nuestro servicio para obtener el heroe, pero con algunos cambios:
this.route.params
.switchMap((params: Params) => this.heroService.getHero(+params['id']))
.subscribe(hero `> this.hero = hero);
Agregaremos una función para obtener un heroe dependiendo del ID en el servicio (ver en heroe.service
) y luego haremos el routeo y el volver => ver en listado.component
y heroe-detalle.component
Ahora que tenemos todo funcionando, nos daremos cuenta que el ngModule
no quedo muy "bonito" y hay muchas cosas que podrian no estar ahí, para eso vamos a hacer las siguiente modificaciones:
Vamos a crear una constante para manejar las rutas
const routes: Routes
Y para que esto funcione, tambien vamos a tener que importar Routes
del mismo lugar que importamos el modulo de ruteo
Y vamos a agregar
exports: [ RouterModule ]
El módulo de enrutamiento agrega RouterModule
a las exportaciones para que los componentes del módulo complementario tengan acceso de enrutador declarable, como RouterLink
y RouterOutlet
.
Dentro de nuestro listado.component
tenemos un ruteo por medio de un link, esto tambien se puede hacer por medio de una función, para ello agregaremos un función para la redirección
ver listado.component.ts
funcion verHeroe()