====== Angular basics ====== ===== Basics of basics ===== ==== Glosario ==== * **Componente**: Clase que interactua con la vista para mostrar datos. Gestiona la lógica de esta. Contiene una directiva y una template. * **Directiva**: La clase encargada de crear e interactuar con los elementos HTML. Cuando Angular encuentra una tag concreta de directiva en la template creará una instancia de esta (por ejemplo ''''). * **Template** es el conjunto de HTML encargado de renderizar una vista. * **Injector** el objeto encargado de buscar la clase adecuada y crearla. * **Módulo**: creamos la aplicación a partir de módulos (nuestros o externos); un módulo es un bloque de código dedicado a algo concreto. Los módulos exportan "algo" y otros módulos lo importan. * **Pipe** es una función que prepara valores para ser mostrados. * **Provider** el encargado de crear una instancia de una dependencia. * **Router** el encargado de crear la navegación entre vistas. * **Routing component** es un componente enlazado a un router. * **Vista** es una porción de ventana para mostrar información e interactuar con el usuario. La vista se renderiza a partir una o varias directivas. El router es el encargado de indicar cuando mostrar una vista. ==== Aplicación básica ==== * {{:wiki2:angular:quickstart-master.zip|Estructura de aplicación básica}} Necesitamos tener una versión de Node.js superior a v5 y de npm superior a v3; para conocer qué versión tenemos de cada uno haremos ''node -v'' y ''npm -v''. Necesitamos los siguientes ficheros: * package.json, que contiene una lista de paquetes necesarios para ejectuar la app. * tsconfig.json, contiene la configuración para el compilador de TypeScript. * typings.json, indica las definiciones de TypeScript. * systemjs.config.js, es la configuración de [[wiki2:jslibrariespages:systemjs|SystemJS]] que es un cargador de módulos (otra alternativa es webpack). Para instalar los paquetes de package.json haremos: * ''$ npm install'' * ''$ npm run typings install'' (si la carpeta typings no aparece). Los comandos de npm útiles son: * ''npm run script-name'' para ejecutar un comando/script. Los importantes no requieren de run. * ''npm start'', inicia el compilador y el servidor a la vez. * ''npm run tsc'', ejecuta el compilador de TypeScript. * ''npm run tsc:w'', ejecuta el compilador de Typescript en watch mode. * ''npm run lite'', ejecuta el servidor. * ''npm run typings'', ejecuta la herramienta typings. * ''npm run postinstall'', ejecuta la instalación de paquetes y definiciones de TypeScript. ==== Notes ==== **Organization**: All the files of the project are created inside a folder called ''app''. Para poder importar una clase, ésta primero se ha de expeortar. ===== TypeScript ===== Classes: export class Hero { id: number; name: string; } Assigning an object to a property: hero: Hero = { id: 1, name: 'Windstorm' }; Una propiedad a la que se le asigna un array vacío: missions: Mission[] = []; Convertir objeto a JSON: return JSON.stringify(this.model); ==== Listas ==== Encontrar un elemento en una lista: getHero(id: number) { return this.getHeroes() .then(heroes => heroes.find(hero => hero.id === id)); } Recoger unos elementos concretos: this.heroService.getHeroes() .then(heroes => this.heroes = heroes.slice(1, 5)); ===== Elements ===== ==== Components ==== Toda aplicación Angular tiene al menos un componente, el root component, que por convención es llamado ''AppComponent''. Un componente es un bloque de código que controla una parte de la vista. Esta es la estructura básica de uno de ellos: import { Component } from '@angular/core'; @Component({ selector: 'my-app', template: '

My First Angular 2 App

' }) export class AppComponent { }
Contiene: (1) imports para hacer referencia a lo que necesita. (2) Decorador @Component para indicar plantillas y propiedades "externas" del componente (metadata que indica como crearlo y usarlo). (3) Su clase. En el decorador nos encontramos con el ''selector'' y la ''template''. El primero es la tag que corresponderá al componente en contreto. El segundo, la template, especifica el HTML que se incrustará cuando tenga que aparecer. === Clase con propiedades === export class AppComponent { title = 'Tour of Heroes'; hero = 'Windstorm'; } === Leer contenido desde la template === Template de una sola línea que lee la propiedad title y la hero: template: '

{{title}}

{{hero}} details!

'
Template de varias líneas (con el acento a la izquierda): template:`

{{title}}

{{hero.name}} details!

{{hero.id}}
{{hero.name}}
`
=== Añadir estilos en el componente === El decorador de componente también admite la propiedad ''styles'': styles: [` .selected { background-color: #CFD8DC !important; color: white; } .heroes { margin: 0 0 2em 0; list-style-type: none; padding: 0; width: 15em; } .heroes li { cursor: pointer; position: relative; left: 0; background-color: #EEE; margin: .5em; padding: .3em 0; height: 1.6em; border-radius: 4px; } .heroes li.selected:hover { background-color: #BBD8DC !important; color: white; } .heroes li:hover { color: #607D8B; background-color: #DDD; left: .1em; } .heroes .text { position: relative; top: -3px; } .heroes .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; } `] También podemos hacerlo desde un fichero externo con: styleUrls: ['app/dashboard.component.css'] Para añadir los estilos globales de toda la app simplemente añadiremos el link al index.html: === Añadir propiedades de entrada === Para ello usamos el decorador ''@Input'' (que necesitamos importar) sobre la propiedad de la clase: import { Component, Input } from '@angular/core'; import { Mission } from './mission'; @Component({ selector: 'mission-detail', template: `
...
` }) export class MissionDetailComponent { @Input() mission: Mission; }
Luego, cuando la queramos usar en una template: :!: Recuerda que para que funcione has de incluir el nuevo selector como directiva en la lista de directivas de ese componente: @Component({ selector: 'newskid-app', template: ` `, styles: [` `], directives: [MissionDetailComponent] }) === Template en fichero === En vez de usar la propiedad ''template'' usaremos la propiedad ''templateUrl'': templateUrl: 'app/dashboard.component.html', ==== Templates for components ==== === ngFor === Podemos indicar que cuando se haga click en un elemento...
  • {{hero.id}} {{hero.name}}
  • ... se llame a un método concreto de la clase: export class AppComponent { title = 'Newskid missions'; missions = MISSIONS; selectedMission: Mission; onSelect(mission: Mission) { this.selectedMission = mission; } } === ngIf === Si existe el elemento....

    {{selectedMission.name}} Edition

    {{selectedMission.id}}
    ==== Bootstrapping ==== Es importante añadir un finchero ''main.ts'' que contenga el bootstrapping de la app: import { bootstrap } from '@angular/platform-browser-dynamic'; import { AppComponent } from './app.component'; bootstrap(AppComponent); Se ha de importar los dos elementos necesarios para lanzar la app: - La función bootstrap. - El componente root de la aplicación. La función ''bootstrap'' viene de platform-browser-dynamic debido a que hay muchas formas de lanzar la app. Una de ellas, por ejemplo, es hacerlo desde el server. ==== El fichero index.html ==== Es el fichero que define la página web y que contiene la app. Se declaran las librerías JavaScript necesarias, la configuración para el SystemJS y la tag de la aplicación (en este caso my-app). Angular 2 QuickStart Loading... ==== Services ==== Para hacer un servicio, una clase que se pueda instanciar automáticamente en el constructor, añadimos ''@Injectable()'' a la clase: import { Injectable } from '@angular/core'; import { MISSIONS } from './mock-missions'; @Injectable() export class MissionService { getMissions () { return Promise.resolve(MISSIONS); } } A partir de ahora puede ser usado en la clase: @Component({ selector: 'newskid-app', ... providers: [MissionService] }) export class AppComponent implements OnInit { ... constructor(private missionService: MissionService) { } Esto agrega una propiedad a la clase denominada ''missionService''. Fíjate que también hay que añadirlo en el array de ''providers''. ==== Interfaces ==== === ngOnInit === import { OnInit } from '@angular/core'; export class AppComponent implements OnInit { ngOnInit() { } } ==== Promises ==== Devolver\declarar una promise (función que hace algo en algún momento, que tarda algún tiempo y que, cuando le sale, devuelve algo): getHeroes() { return Promise.resolve(HEROES); } Usar lo que devuelve (HEROES es un array): getHeroes() { this.heroService.getHeroes().then(heroes => this.heroes = heroes); } ==== Routing ==== AppComponent únicamente debería manejar la navegación. El objeto router es el encargado de gestionarla y es una combinación del ''provideRouter'', directivas y configuración. Es necesario tener en el index.html: Crearemos un fichero ''app.routes.ts'' donde añadiremos todas las rutas: import { provideRouter, RouterConfig } from '@angular/router'; import { HeroesComponent } from './heroes.component'; const routes: RouterConfig = [ { path: 'heroes', component: HeroesComponent } ]; export const appRouterProviders = [ provideRouter(routes) ]; La ''RouterConfig'' es un array de definiciones de ruta. Aquí únicamente tenemos una definición, pero podemos añadir más. Una difinición tiene un path (el path con el que hace match la URL, en este caso ''/heroes'') y un componente (el componente que se crea en esa ruta). El router ha de ser importado en el bootstrap: import { bootstrap } from '@angular/platform-browser-dynamic'; import { AppComponent } from './app.component'; import { appRouterProviders } from './app.routes'; bootstrap(AppComponent, [ appRouterProviders ]); Una de las directivas de router (''ROUTER_DIRECTIVES'') es ''''. Si lo añadimos en una template (ejemplo de app.component): import { Component } from '@angular/core'; import { ROUTER_DIRECTIVES } from '@angular/router'; import { HeroService } from './hero.service'; @Component({ selector: 'my-app', template: `

    {{title}}

    Heroes `, directives: [ROUTER_DIRECTIVES], providers: [ HeroService ] }) export class AppComponent { title = 'Tour of Heroes'; }
    También podemos crear una redirección: const routes: RouterConfig = [ { path: 'missions', component: MissionsComponent }, { path: '', redirectTo:'/dashboard', pathMatch: 'full' }, { path: 'dashboard', component: DashboardComponent } ]; O parametrizar una ruta (por ejemplo ''/detail/11''): { path: 'detail/:id', component: HeroDetailComponent }, Entonces tendremos que recoger el id dentro del componente donde se vaya a leer, para ello leemos de la url en el navegador: import { Component, OnInit, OnDestroy } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Hero } from './hero'; import { HeroService } from './hero.service'; @Component({ selector: 'my-hero-detail', templateUrl: 'app/hero-detail.component.html', }) export class HeroDetailComponent implements OnInit, OnDestroy { hero: Hero; sub: any; constructor( private heroService: HeroService, private route: ActivatedRoute) { } ngOnInit() { this.sub = this.route.params.subscribe(params => { let id = +params['id']; /* convert id to int */ this.heroService.getHero(id) .then(hero => this.hero = hero); }); } ngOnDestroy() { this.sub.unsubscribe(); } goBack() { window.history.back(); } } - Necesitamos un observer de la url, para ello accedemos a la ActivatedRoute - Nos aseguramos que nos subscrivimos a la ActivatedRoute en el ngOnInit y que en el ngOnDestroy nos desubscribimos. - Necesitamos agregarlo al constructor - En el ngOnInit nos subscribimos a los ''params'' para obtener la ''id'' de la ruta actual (ActivateRoute); luego se usará el servicio para obtener ese id concreto. ==== Http ====