Angular2 workshop
Gonzalo Ruiz de Villa
@gruizdevilla
https://goo.gl/74t6CF
AngularJS Madrid
La url de esta presentación se emitirá durante todo el workshop.
https://goo.gl/74t6CF
'Angular 1 was a framework, Angular 2 is a platform' The father of Angular Misko Hevery
https://goo.gl/74t6CF
Web
Mobile Web
Android
iOS
Windows
Mac
Linux
https://goo.gl/74t6CF
Progressive Web Apps
https://goo.gl/74t6CF
- pensado para construir aplicaciones del tamaño que sea
- mobile first
- rendimiento brutal
- sencillo y expresivo
- inmutabilidad
- inyección de dependencias jerárquica
- integración con webcomponents
- i18n
- server side rendering
- web workers
- aplicaciones nativas, de escritorio
- PWA
- Angular CLI
¿Por qué Angular 2?
https://goo.gl/74t6CF
Empecemos
https://goo.gl/74t6CF
1. Configuremos
el entorno
https://goo.gl/74t6CF
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": false
}
}
tsconfig.json
Indicamos como funcionar al compilador de TypeScript
https://goo.gl/74t6CF
{
"globalDependencies": {
"core-js": "registry:dt/core-js#0.0.0+20160317120654",
"jasmine": "registry:dt/jasmine#2.2.0+20160505161446",
"node": "registry:dt/node#4.0.0+20160509154515"
}
}
typings.json
Agregamos definiciones que TypeScript no reconoce de forma nativa
Nota: si tienes que integrarte con alguna librería existente, si es popular, probablemente encuentre los typings en https://github.com/DefinitelyTyped/DefinitelyTyped
{
"name": "angular2-workshop",
"version": "1.0.0",
"scripts": {
"start": "tsc && concurrently \"npm run tsc:w\" \"npm run lite\" ",
"lite": "lite-server",
"postinstall": "typings install",
"tsc": "tsc",
"tsc:w": "tsc -w",
"typings": "typings"
},
"license": "ISC",
"dependencies": {
"@angular/common": "2.0.0-rc.1",
"@angular/compiler": "2.0.0-rc.1",
"@angular/core": "2.0.0-rc.1",
"@angular/http": "2.0.0-rc.1",
"@angular/platform-browser": "2.0.0-rc.1",
"@angular/platform-browser-dynamic": "2.0.0-rc.1",
"@angular/router": "2.0.0-rc.1",
"@angular/router-deprecated": "2.0.0-rc.1",
"@angular/upgrade": "2.0.0-rc.1",
"systemjs": "0.19.27",
"core-js": "^2.4.0",
"reflect-metadata": "^0.1.3",
"rxjs": "5.0.0-beta.6",
"zone.js": "^0.6.12",
"angular2-in-memory-web-api": "0.0.10",
"bootstrap": "^3.3.6"
},
"devDependencies": {
"concurrently": "^2.0.0",
"lite-server": "^2.2.0",
"typescript": "^1.8.10",
"typings":"^1.0.4"
}
}
package.json
Configuramos el proyecto
/**
* System configuration for Angular 2 samples
* Adjust as necessary for your application needs.
*/
(function(global) {
// map tells the System loader where to look for things
var map = {
'app': 'app', // 'dist',
'@angular': 'node_modules/@angular',
'rxjs': 'node_modules/rxjs'
};
// packages tells the System loader how to load when no filename and/or no extension
var packages = {
'app': { main: 'main.js', defaultExtension: 'js' },
'rxjs': { defaultExtension: 'js' }
};
var ngPackageNames = [
'common',
'compiler',
'core',
'http',
'platform-browser',
'platform-browser-dynamic',
'router',
'router-deprecated',
'upgrade',
];
// Add package entries for angular packages
ngPackageNames.forEach(function(pkgName) {
packages['@angular/'+pkgName] = { main: pkgName + '.umd.js', defaultExtension: 'js' };
});
var config = {
map: map,
packages: packages,
baseURL: '/'
};
System.config(config);
})(this);
systemjs.config.js
Configuramos systemjs
Casi estamos listos para implementar un hola mundo
> npm install
Instalamos las dependencias
Ahora si podemos empezar tirar código 😊
https://goo.gl/74t6CF
2. Hola mundo o si mi entorno ya funciona
https://goo.gl/74t6CF
import {Component} from '@angular/core';
@Component({
selector: 'my-app',
template: '<h1>Mi primera app: chispas.✨</h1>'
})
export class AppComponent { }
app/app.component.ts
¡Mi primer componente, chispas! ✨
https://goo.gl/74t6CF
import { bootstrap } from '@angular/platform-browser-dynamic';
import { AppComponent } from './app.component';
bootstrap(AppComponent);
app/main.ts
Donde decimos que hay que arrancar
¿'@angular/platform-browser-dynamic'? ¿por qué no está en '@angular/core'?
Como se arranca una aplicación es específico de la plataforma. Otras opciones pueden ser NativeScript, Apache Cordoba o incluso el servidor.
<html>
<head>
<title>Angular 2 Workshop</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/styles.css">
<!-- 1. Load libraries -->
<!-- Polyfill(s) for older browsers -->
<script src="/node_modules/core-js/client/shim.min.js"></script>
<script src="/node_modules/zone.js/dist/zone.js"></script>
<script src="/node_modules/reflect-metadata/Reflect.js"></script>
<script src="/node_modules/systemjs/dist/system.src.js"></script>
<!-- 2. Configure SystemJS -->
<script src="/systemjs.config.js"></script>
<script>
System.import('app').catch(function(err){ console.error(err); });
</script>
<base href="/">
</head>
<!-- 3. Display the application -->
<body>
<my-app>Loading...</my-app>
</body>
</html>
index.html
index.html
- Polyfills de navegadores
- angular2 polyfills: zones y reflect-metadata para decoradores
- SystemJS como module loader, aunque podrías usar otro como webpack
https://goo.gl/74t6CF
/* Master Styles */
h1 {
color: #369;
font-family: Arial, Helvetica, sans-serif;
font-size: 250%;
}
h2, h3 {
color: #444;
font-family: Arial, Helvetica, sans-serif;
font-weight: lighter;
}
body {
margin: 2em;
}
body, input[text], button {
color: #888;
font-family: Cambria, Georgia;
}
a {
cursor: pointer;
cursor: hand;
}
button {
font-family: Arial;
background-color: #eee;
border: none;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
cursor: hand;
}
button:hover {
background-color: #cfd8dc;
}
button:disabled {
background-color: #eee;
color: #aaa;
cursor: auto;
}
/* Navigation link styles */
nav a {
padding: 5px 10px;
text-decoration: none;
margin-top: 10px;
display: inline-block;
background-color: #eee;
border-radius: 4px;
}
nav a:visited, a:link {
color: #607D8B;
}
nav a:hover {
color: #039be5;
background-color: #CFD8DC;
}
nav a.router-link-active {
color: #039be5;
}
/* items class */
.items {
margin: 0 0 2em 0;
list-style-type: none;
padding: 0;
width: 24em;
}
.items li {
cursor: pointer;
position: relative;
left: 0;
background-color: #EEE;
margin: .5em;
padding: .3em 0;
height: 1.6em;
border-radius: 4px;
}
.items li:hover {
color: #607D8B;
background-color: #DDD;
left: .1em;
}
.items li.selected:hover {
background-color: #BBD8DC;
color: white;
}
.items .text {
position: relative;
top: -3px;
}
.items {
margin: 0 0 2em 0;
list-style-type: none;
padding: 0;
width: 24em;
}
.items li {
cursor: pointer;
position: relative;
left: 0;
background-color: #EEE;
margin: .5em;
padding: .3em 0;
height: 1.6em;
border-radius: 4px;
}
.items li:hover {
color: #607D8B;
background-color: #DDD;
left: .1em;
}
.items li.selected {
background-color: #CFD8DC;
color: white;
}
.items li.selected:hover {
background-color: #BBD8DC;
}
.items .text {
position: relative;
top: -3px;
}
.items .badge {
display: inline-block;
font-size: small;
color: white;
padding: 0.8em 0.7em 0 0.7em;
background-color: #607D8B;
line-height: 1em;
position: relative;
left: -1px;
top: -4px;
height: 1.8em;
margin-right: .8em;
border-radius: 4px 0 0 4px;
}
/* everywhere else */
* {
font-family: Arial, Helvetica, sans-serif;
}
styles.css
https://goo.gl/74t6CF
> npm start
🍀
https://goo.gl/74t6CF
https://goo.gl/74t6CF
3. Introduciendo el modelo estándar
y los componentes de Angular
https://goo.gl/74t6CF
Un poco de interpolación.
import {Component} from '@angular/core';
@Component({
selector: 'my-app',
template: '<h1>{{titulo}}</h1>'
})
export class AppComponent {
titulo = 'Modelo estándar';
}
app/app.component.ts
https://goo.gl/74t6CF
//...
export class Particle {
id: string;
name: string;
mass: string;
charge: string;
spin: string;
type: ParticleType;
forces: Force[];
}
type ParticleType = 'Quark' | 'Lepton' | 'Boson';
type Force = 'Strong' | 'Electromagnetic' | 'Weak';
app/app.component.ts
https://goo.gl/74t6CF
Agreguemos una clase
particle: Particle = {
id: 'u',
name: 'up',
mass: '2.3MeV/c²',
charge: '2/3',
spin: '1/2',
type: 'Quark',
forces: ['Electromagnetic', 'Strong', 'Weak']
};
app/app.component.ts
Agregamos la propiedad a AppComponent
template: '<h1>{{titulo}}</h1><p>Los detalles del {{particle.type}} <b>{{particle.name}}.</b></p>'
Y cambiamos el template
https://goo.gl/74t6CF
template: `
<h1>{{titulo}}</h1>
<h2>Detalles del {{particle.type}} {{particle.name}}.</h2>
<p>Masa: {{particle.mass}}</p>
<p>Carga: {{particle.charge}}</p>
<p>Spin: {{particle.spin}}</p>
<p>Type: {{particle.type}}</p>
<p>Fuerzas: {{particle.forces.join(', ')}}</p>
`
app/app.component.ts
El backtick ` nos permite un template más manejable
https://goo.gl/74t6CF
template: `
<h1>{{titulo}}</h1>
<h2>Detalles del {{particle.type}} {{particle.name}}.</h2>
<p>Masa: {{particle.mass}}</p>
<p>Carga: {{particle.charge}}</p>
<p>Spin: {{particle.spin}}</p>
<p>Type: {{particle.type}}</p>
<p>Fuerzas: {{particle.forces.join(', ')}}</p>
<div>
<label>Ajusta la masa: </label>
<input value="{{particle.mass}}" placeholder="masa">
</div>
`
app/app.component.ts
Ajustando la masa
https://goo.gl/74t6CF
template: `
<h1>{{titulo}}</h1>
<h2>Detalles del {{particle.type}} {{particle.name}}.</h2>
<p>Masa: {{particle.mass}}</p>
<p>Carga: {{particle.charge}}</p>
<p>Spin: {{particle.spin}}</p>
<p>Type: {{particle.type}}</p>
<p>Fuerzas: {{particle.forces.join(', ')}}</p>
<div>
<label>Ajusta la masa: </label>
<input [(ngModel)]="particle.mass" placeholder="masa">
</div>
`
app/app.component.ts
Double binding
https://goo.gl/74t6CF
4. El listado de partículas fundamentales
y un master-detail con Angular
https://goo.gl/74t6CF
//...
var PARTICLES: Particle[] = [
{ id: 'u', name: 'up', mass: '2.3MeV/c²', charge: '2/3', spin: '1/2', type: 'Quark', forces: ['Electromagnetic', 'Strong', 'Weak'] },
{ id: 'd', name: 'down', mass: '4.8MeV/c²', charge: '1/3', spin: '1/2', type: 'Quark', forces: ['Electromagnetic', 'Strong', 'Weak'] },
{ id: 'c', name: 'charm', mass: '1.275GeV/c²', charge: '2/3', spin: '1/2', type: 'Quark', forces: ['Electromagnetic', 'Strong', 'Weak'] },
{ id: 's', name: 'strange', mass: '95MeV/c²', charge: '1/3', spin: '1/2', type: 'Quark', forces: ['Electromagnetic', 'Strong', 'Weak'] },
{ id: 't', name: 'top', mass: '173.07GeV/c²', charge: '2/3', spin: '1/2', type: 'Quark', forces: ['Electromagnetic', 'Strong', 'Weak'] },
{ id: 'b', name: 'bottom', mass: '4.18GeV/c²', charge: '1/3', spin: '1/2', type: 'Quark', forces: ['Electromagnetic', 'Strong', 'Weak'] },
{ id: 'e', name: 'electron', mass: '0.511MeV/c²', charge: '-1', spin: '1/2', type: 'Lepton', forces: ['Electromagnetic', 'Weak'] },
{ id: 'μ', name: 'muon', mass: '105.7MeV/c²', charge: '-1', spin: '1/2', type: 'Lepton', forces: ['Electromagnetic', 'Weak'] },
{ id: 'τ', name: 'tau', mass: '1.777GeV/c²', charge: '-1', spin: '1/2', type: 'Lepton', forces: ['Electromagnetic', 'Weak'] },
{ id: 'νe', name: 'electron neutrino', mass: '<2.2eV/c²', charge: '0', spin: '1/2', type: 'Lepton', forces: ['Weak'] },
{ id: 'νμ', name: 'muon neutrino', mass: '<0.17MeV/c²', charge: '0', spin: '1/2', type: 'Lepton', forces: ['Weak'] },
{ id: 'ντ', name: 'tau neutrino', mass: '<15.5MeV/c²', charge: '0', spin: '1/2', type: 'Lepton', forces: ['Weak'] },
{ id: 'g', name: 'gluon', mass: '0', charge: '0', spin: '1', type: 'Boson', forces: ['Strong'] },
{ id: 'γ', name: 'photon', mass: '0', charge: '0', spin: '1', type: 'Boson', forces: ['Electromagnetic'] },
{ id: 'Z', name: 'Z boson', mass: '0', charge: '0', spin: '1', type: 'Boson', forces: ['Weak'] },
{ id: 'W', name: 'W boson', mass: '0', charge: '0', spin: '±1', type: 'Boson', forces: ['Weak'] },
{ id: 'H', name: 'Higgs boson', mass: '0', charge: '0', spin: '0', type: 'Boson', forces: [] }
];
app/app.component.ts
El listado de partículas
app/app.component.ts
export class AppComponent {
titulo = 'Modelo estándar';
public particles = PARTICLES;
}
Agregamos las partículas al componente
https://goo.gl/74t6CF
app/app.component.ts
<ul class="particles">
<li *ngFor="let particle of particles">
<span class="code">{{particle.id}}</span>
{{particle.name}}
</li>
</ul>
Y agregamos al template:
Si la página deja de funcionar, mira la consola. Lo que referencia a la propiedad 'particle' de AppComponent está fallando con un null pointer (por ejemplo, "Cannot read property 'type' of undefined").
https://goo.gl/74t6CF
app/app.component.ts
<div *ngIf="particle">
<h2>Detalles del {{particle.type}} {{particle.name}}.</h2>
<p>Masa: {{particle.mass}}</p>
<p>Carga: {{particle.charge}}</p>
<p>Spin: {{particle.spin}}</p>
<p>Type: {{particle.type}}</p>
<p>Fuerzas: {{particle.forces.join(', ')}}</p>
<div>
<label>Ajusta la masa: </label>
<input [(ngModel)]="particle.mass" placeholder="masa">
</div>
</div>
Evitemos el null pointer.
https://goo.gl/74t6CF
app/app.component.ts
styles:[`
.selected {
background-color: #CFD8DC !important;
color: white;
}
.particles {
margin: 0 0 2em 0;
list-style-type: none;
padding: 0;
width: 15em;
float: left;
}
.particles li {
cursor: pointer;
position: relative;
left: 0;
background-color: #EEE;
margin: .5em;
padding: .3em 0;
height: 1.6em;
border-radius: 20px 4px 4px 20px;
}
.particles li.selected:hover {
background-color: #BBD8DC !important;
color: white;
}
.particles li:hover {
color: #607D8B;
background-color: #DDD;
left: .1em;
}
.particles .text {
position: relative;
top: -3px;
}
.particles .code {
display: inline-block;
text-align: center;
font-size: small;
color: white;
width: 1.2em;
padding: 0.8em 0.7em 0 0.7em;
background-color: #607D8B;
line-height: 1em;
position: relative;
left: -1px;
top: -4px;
height: 1.8em;
margin-right: .8em;
border-radius: 20px;
}
`]
Agreguemos unos estilos en @Component
app/app.component.ts
<li *ngFor="let particle of particles" (click)="onSelect(particle)">
Bind al evento click del li
La sintaxis (evento)="expresion", agrega un listener sobre evento, y evalúa expresion cuando se produce.
https://goo.gl/74t6CF
app/app.component.ts
selectedParticle: Particle;
Guardaremos la partícula seleccionada
onSelect(particle: Particle) { this.selectedParticle = particle; }
y definimos onSelect
https://goo.gl/74t6CF
app/app.component.ts
<div *ngIf="selectedParticle">
<h2>Detalles del {{selectedParticle.type}} {{selectedParticle.name}}.</h2>
<p>Masa: {{selectedParticle.mass}}</p>
<p>Carga: {{selectedParticle.charge}}</p>
<p>Spin: {{selectedParticle.spin}}</p>
<p>Type: {{selectedParticle.type}}</p>
<p>Fuerzas: {{selectedParticle.forces.join(', ')}}</p>
<div>
<label>Ajusta la masa: </label>
<input [(ngModel)]="selectedParticle.mass" placeholder="masa">
</div>
</div>
En la plantilla, pintamos la partícula seleccionada con
https://goo.gl/74t6CF
app/app.component.ts
[class.selected]="particle === selectedParticle"
Podemos marcar el elemento li seleccionado con
https://goo.gl/74t6CF
4. Partículas compuestas
o creando componentes con Angular
https://goo.gl/74t6CF
El código está quedando farragoso, por lo que el siguiente paso es cortarlo en partes más manejables, mantenibles y fáciles de entender.
https://goo.gl/74t6CF
app/particle-detail.component.ts
import {Component} from '@angular/core';
@Component({
selector: 'my-particle-detail',
})
export class ParticleDetailComponent {
}
Nuevo componente en fichero nuevo.
https://goo.gl/74t6CF
app/particle-detail.component.ts
template:` <div *ngIf="particle">
<h2>Detalles del {{particle.type}} {{particle.name}}.</h2>
<p>Masa: {{particle.mass}}</p>
<p>Carga: {{particle.charge}}</p>
<p>Spin: {{particle.spin}}</p>
<p>Type: {{particle.type}}</p>
<p>Fuerzas: {{particle.forces.join(', ')}}</p>
<div>
<label>Ajusta la masa: </label>
<input [(ngModel)]="particle.mass" placeholder="masa">
</div>
</div>
`
Nos traemos su fragmento de template (cambiando selectedParticle por particle).
https://goo.gl/74t6CF
app/particle-detail.component.ts
particle: Particle;
Agregamos la propiedad
https://goo.gl/74t6CF
app/particle.ts
export class Particle {
id: string;
name: string;
mass: string;
charge: string;
spin: string;
type: ParticleType;
forces: Force[];
}
export type ParticleType = 'Quark' | 'Lepton' | 'Boson';
export type Force = 'Strong' | 'Electromagnetic' | 'Weak';
Definimos la partícula de forma centralizada
https://goo.gl/74t6CF
app/app.component.ts
import {Particle} from './particle';
y agregamos los correspondientes imports
app/particle-detail.component.ts
https://goo.gl/74t6CF
app/app.component.ts
<my-particle-detail [particle]="selectedParticle"></my-particle-detail>
Usamos detail "my-particle-detail" en la plantilla:
https://goo.gl/74t6CF
app/particle-detail.component.ts
@Input()
particle: Particle;
Anotamos una propiedad del componente para que pueda recibir datos.
(*cuidado con dejarse los paréntesis en la anotación)
import {Component, Input} from '@angular/core';
y hay que cambiar el import
https://goo.gl/74t6CF
app/app.component.ts
import {ParticleDetailComponent} from './particle-detail.component';
Importamos el componente particle-detail para poder usarlo
https://goo.gl/74t6CF
directives: [ParticleDetailComponent]
Y le informamos a @Component de que tiene que usarlo al interpretar la plantilla
4. Fabricando partículas
desde servicios de Angular
https://goo.gl/74t6CF
app/particle.service.ts
import {Injectable} from '@angular/core';
@Injectable()
export class ParticleService {
}
Creamos un servicio que devuelva la información necesaria.
https://goo.gl/74t6CF
app/particle.service.ts
export class ParticleService {
getParticles(){
return PARTICLES;
}
}
Le dotamos de contenido:
import {PARTICLES} from './mock-particles';
import {Particle} from './particle';
export var PARTICLES: Particle[] = [
{ id: 'u', name: 'up', mass: '2.3MeV/c²', charge: '2/3', spin: '1/2', type: 'Quark', forces: ['Electromagnetic', 'Strong', 'Weak'] },
{ id: 'd', name: 'down', mass: '4.8MeV/c²', charge: '1/3', spin: '1/2', type: 'Quark', forces: ['Electromagnetic', 'Strong', 'Weak'] },
//...
app/mock-particles.ts
app/app.component.ts
Importamos el servicio
import {ParticleService} from './particle.service';
constructor(private _particleService: ParticleService) { }
Agregamos la dependecias
En @Component agregamos el provedor para indicar al sistema de DI de donde sacar el servicio
providers: [ParticleService]
https://goo.gl/74t6CF
El componente tiene un ciclo de vida: ngOnInit, ngOnChanges, ngDoCheck, ngOnDestroy
En ngOnInit inicializamos la propiedad particles
app/app.component.ts
export class AppComponent {
ngOnInit() {
this.getParticles();
}
getParticles() {
this.particles = this._particleService.getParticles();
}
}
https://goo.gl/74t6CF
El servicio debería ser asíncrono
app/particle.service.ts
getParticles() {
return Promise.resolve(PARTICLES);
}
getParticlesSlowly() {
return new Promise<Particle[]>(resolve =>
setTimeout(()=>resolve(PARTICLES), 2000)
);
}
https://goo.gl/74t6CF
Al resolver la promesa guardamos las partículas recibidas
app/app.component.ts
getParticles(){
this._particleService.getParticles().then(
particles => this.particles = particles
)
}
https://goo.gl/74t6CF
5. Navegando por la realidad
y las rutas con Angular
https://goo.gl/74t6CF
Vamos a agregar tres estados a la aplicación:
- Vista por familias
- Vista de listado
- Vista de detalle
https://goo.gl/74t6CF
app/app.component.ts
app/particles.component.ts
renombramos
como
cambiamos el nombre de la clase ParticlesComponent y el selector a my-particles
https://goo.gl/74t6CF
app/app.component.ts
como
import { Component } from '@angular/core';
import { ParticleService } from './particle.service';
import { ParticlesComponent } from './particles.component';
@Component({
selector: 'my-app',
template: `
<h1>{{title}}</h1>
<my-particles></my-particles>
`,
directives: [ParticlesComponent],
providers: [
ParticleService
]
})
export class AppComponent {
titulo = 'Modelo estándar';
}
Elimina el provider de particles.component.ts
index.html
agregamos la referencia al módulo de routing
<base href="/">
https://goo.gl/74t6CF
app/app.component.ts
Agregamos una navegación en app component
import { Component } from '@angular/core';
import { ParticleService } from './particle.service';
import { ParticlesComponent } from './particles.component';
import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS }
from '@angular/router-deprecated';
@RouteConfig([
{
path: '/particles',
name: 'Particles',
component: ParticlesComponent
}
])
Configuramos la ruta
app/app.component.ts
Agregamos un enlace para navegar y un lugar donde pintar el contenido
template: `
<h1>{{title}}</h1>
<a [routerLink]="['Particles']">Particles</a>
<a [routerLink]="['Families']">Families</a>
<router-outlet></router-outlet>
`,
app/app.component.ts
Configuramos la inyección de dependencias de la aplicación
directives: [ROUTER_DIRECTIVES],
providers: [
ROUTER_PROVIDERS,
ParticleService
]
app/particles.component.ts
Al hacer click navegaremos al detalle como nuevo estado
<h1>{{titulo}}</h1>
<ul class="particles">
<li *ngFor="let particle of particles"
(click)="onSelect(particle)"
[class.selected]="particle === selectedParticle">
<span class="code">{{particle.id}}</span>
{{particle.name}}
</li>
</ul>
<div *ngIf="selectedParticle">
<p>Selected particle: {{selectedParticle.type}} {{selectedParticle.name}}</p>
<button (click)="gotoDetail()">Ir al detalle</button>
</div>
templateUrl: 'app/particles.component.html',
styleUrls: ['app/particles.component.css']
app/particles.component.html
app/particles.component.css
Metemos lo que había en la propiedad styles
app/app.component.ts
Y agregamos una nueva anotación a la clase para configurar las rutas
@RouteConfig([
{path:'/families', name: 'Families', component: FamiliesComponent},
{path:'/particles', name: 'Particles', component: ParticlesComponent, useAsDefault: true}
])
export class AppComponent {
import {Component} from '@angular/core';
@Component({
template: 'Families'
})
export class FamiliesComponent {
}
lo dejamos de momento muy básico (para desarrollar en otro momento)
app/families.component.ts
Para navegar a Detail hay que registrar la ruta y llamar al router
constructor(
private _router: Router,
private _particleService: ParticleService
) {}
app/app.component.ts
app/particles.component.ts
{path: '/detail/:id', name: 'ParticleDetail', component: ParticleDetailComponent }
gotoDetail(){
this._router.navigate(['ParticleDetail', { id: this.selectedParticle.id }]);
}
import {Router} from '@angular/router-deprecated';
Detail debe sacar el id de la partícula de la ruta
app/particle-detail.component.ts
import {Component} from '@angular/core';
import {Particle} from './particle';
import {ParticleService} from './particle.service'
import {RouteParams} from 'angular2/router';
constructor(
private _particleService: ParticleService,
private _routeParams: RouteParams) {
}
Pedimos los dos servicios necesarios
Detail debe sacar el id de la partícula de la ruta
app/particle-detail.component.ts
ngOnInit() {
let id = this._routeParams.get('id');
this._particleService.getParticle(id)
.then(particle => this.particle = particle);
}
Agregamos el método que falta
app/particle.service.ts
getParticle(id) {
return this.getParticles().then(
particles => particles.filter(
particle => particle.id === id
)[0]
)
}
https://goo.gl/74t6CF
Volver atrás
app/particle.component.ts
goBack() {
window.history.back();
}
https://goo.gl/74t6CF
6. Teletransportación
y las peticiones $http con Angular
https://goo.gl/74t6CF
mock/particles.json
[
{ "id": "u", "name": "up", "mass": "2.3MeV/c²", "charge": "2/3", "spin": "1/2", "type": "Quark", "forces": ["Electromagnetic", "Strong", "Weak"] },
{ "id": "d", "name": "down", "mass": "4.8MeV/c²", "charge": "1/3", "spin": "1/2", "type": "Quark", "forces": ["Electromagnetic", "Strong", "Weak"] },
{ "id": "c", "name": "charm", "mass": "1.275GeV/c²", "charge": "2/3", "spin": "1/2", "type": "Quark", "forces": ["Electromagnetic", "Strong", "Weak"] },
{ "id": "s", "name": "strange", "mass": "95MeV/c²", "charge": "1/3", "spin": "1/2", "type": "Quark", "forces": ["Electromagnetic", "Strong", "Weak"] },
{ "id": "t", "name": "top", "mass": "173.07GeV/c²", "charge": "2/3", "spin": "1/2", "type": "Quark", "forces": ["Electromagnetic", "Strong", "Weak"] },
{ "id": "b", "name": "bottom", "mass": "4.18GeV/c²", "charge": "1/3", "spin": "1/2", "type": "Quark", "forces": ["Electromagnetic", "Strong", "Weak"] },
{ "id": "e", "name": "electron", "mass": "0.511MeV/c²", "charge": "-1", "spin": "1/2", "type": "Lepton", "forces": ["Electromagnetic", "Weak"] },
{ "id": "μ", "name": "muon", "mass": "105.7MeV/c²", "charge": "-1", "spin": "1/2", "type": "Lepton", "forces": ["Electromagnetic", "Weak"] },
{ "id": "τ", "name": "tau", "mass": "1.777GeV/c²", "charge": "-1", "spin": "1/2", "type": "Lepton", "forces": ["Electromagnetic", "Weak"] },
{ "id": "νe", "name": "electron neutrino", "mass": "<2.2eV/c²", "charge": "0", "spin": "1/2", "type": "Lepton", "forces": ["Weak"] },
{ "id": "νμ", "name": "muon neutrino", "mass": "<0.17MeV/c²", "charge": "0", "spin": "1/2", "type": "Lepton", "forces": ["Weak"] },
{ "id": "ντ", "name": "tau neutrino", "mass": "<15.5MeV/c²", "charge": "0", "spin": "1/2", "type": "Lepton", "forces": ["Weak"] },
{ "id": "g", "name": "gluon", "mass": "0", "charge": "0", "spin": "1", "type": "Boson", "forces": ["Strong"] },
{ "id": "γ", "name": "photon", "mass": "0", "charge": "0", "spin": "1", "type": "Boson", "forces": ["Electromagnetic"] },
{ "id": "Z", "name": "Z boson", "mass": "0", "charge": "0", "spin": "1", "type": "Boson", "forces": ["Weak"] },
{ "id": "W", "name": "W boson", "mass": "0", "charge": "0", "spin": "±1", "type": "Boson", "forces": ["Weak"] },
{ "id": "H", "name": "Higgs boson", "mass": "0", "charge": "0", "spin": "0", "type": "Boson", "forces": [] }
]
https://goo.gl/74t6CF
app/main.ts
import { bootstrap } from '@angular/platform-browser-dynamic';
import { HTTP_PROVIDERS } from '@angular/http';
import { AppComponent } from './app.component';
bootstrap(AppComponent, [ HTTP_PROVIDERS ]);
https://goo.gl/74t6CF
app/hero.service.ts
Completamos el servicio con la llamada http
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/toPromise';
La importación de 'toPromise' agrega automáticamente el método a Observable
app/hero.service.ts
getHeroes(): Promise<Hero[]> {
return this.http.get(this.heroesUrl)
.toPromise()
.then(response => response.json().data)
.catch(this.handleError);
}
private handleError(error: any) {
console.error('An error occurred', error);
return Promise.reject(error.message || error);
}
Completamos el servicio con la llamada http
private particlesUrl = 'app/particles.json';
constructor(private http: Http) { }
El servicio http devuelve un observable y no una promesa
https://goo.gl/74t6CF
Algunos recursos
-
Building installable mobile apps:
-
Building desktop apps
- Weather Underground on Angular 2 blog
¡Gracias!
Gonzalo Ruiz de Villa
AngularJS Madrid
JSDayES 2016
https://goo.gl/74t6CF
Angular 2 workshop
By Gonzalo Ruiz de Villa
Angular 2 workshop
Slides para el workshop de Angular2
- 17,023