Angular

UTN Rosario

TTADS

Técnicas y Tecnologías Avanzadas de Desarrollo de Software

Ing. Andres Otaduy Ing. Adrian Meca

HTML + HTTP Paginas Estaticas

Primer pagina web Emulador

Evolucion FrontEnd

HTML + Imagenes + Tablas

Hypermedia: HTML + Imagenes + Tablas

Web Applications

Perl, ASP, PHP, Apache, MySQL

Single Page Applications

NodeJs, Java, C#, MongoDB, Javascript, REST API

SPA: Single Page Applications

Javascript

Javascript

  • Uno de los lenguajes mas usados e incomprendidos del mundo
  • Obligatorio para programar en el browser
  • Interfaz con el DOM
  • Sintaxis de C, Semantica de Scheme

Javascript influencias

  • Scheme
  • Java
  • Self
  • Perl

Correr JS

  • Embebido
  • Scripts
  • NodeJs

Javascript Good Parts

  • Loose Typing
  • Objetos Dinamicos
  • Funciones de Orden Superior
  • Funciones Lambda
  • Funciones Clousures
  • Objetos Literales
  • Herencia prototipica
  • Es facil!

Funciones de Orden Superior


function paraCada(unArray, unaFuncion) {
  for (i=0; i < unArray.length; i++) {
    unaFuncion(unArray[i], i);
  }
}

paraCada([10,20,'hola', 'que', 87], function(elem, index) {
  console.log('Elemento:', elem, index)
});

function mapear(unArray, unaFuncion) {
  var answer = [];
  for (i=0; i < unArray.length; i++) {
    answer.push(unaFuncion(unArray[i], i));
  }
  return answer;
}

function cuad(a) {
  return a*a;
}

console.log(mapear([1,2,3,4,5], cuad));
                

Funciones Lambda

var cuad, cubo, array = [1,2,3,4];
cuad = function(a){
  return a * a;
};
cubo = function(a) {
  return cuad(a) * a;
};
var array = [3,5,7,9,11,14];
array.map(cuad) + array.map(cubo);
console.log(array.reduce(0, suma));

array.filter(function (elem){ return elem > 10})
    .map(function (elem) { return elem * elem });

function suma(a,b) {
  return a + b;
}

Clousures

function makeAdder(a) {
  return function(b) {
    return a + b;
  };
}
var x = makeAdder(5);
var y = makeAdder(20);
x(6); // ?
y(7); // ?

Objetos dinamicos

var object = {name: 'Jon'};
object.lastName = 'Smith';
object.print = function(){
  console.log('Name:', this.name);
  console.log('LastName:', this.lastName)};
object.print();

Objetos Literales

{
  "glossary":{
    "title":"example glossary",
    "GlossDiv":{
      "title":"S",
      "GlossList":{
        "GlossEntry":{
          "ID":"SGML",
          "SortAs":"SGML",
          "GlossTerm":"Standard Generalized Markup Language",
          "Acronym":"SGML",
          "Abbrev":"ISO 8879:1986",
          "GlossDef":{
            "para":"A meta-markup language, used to create markup languages such as DocBook.",
            "GlossSeeAlso":[
              "GML",
              "XML"
            ]
          },
          "GlossSee":"markup"
        }
      }
    }
  }
}

Herencia Prototipica


var padre = {nombre: 'Pepe', apellido: 'Perez'};
var hijo = Object.create(padre);
padre.mostrarNombre = function() { console.log(this.nombre)};
hijo.nombre = 'Pedro';
padre.mostrarNombre();
hijo.mostrarNombre();
console.log(hijo.apellido);

Clases con Herencia Prototipica

// Shape - superclass
  function Shape() {
    this.x = 0;
    this.y = 0;
  }

  // superclass method
  Shape.prototype.move = function(x, y) {
    this.x += x;
    this.y += y;
    console.info('Shape moved.');
  };

  // Rectangle - subclass
  function Rectangle() {
    Shape.call(this); // call super constructor.
  }

  // subclass extends superclass
  Rectangle.prototype = Object.create(Shape.prototype);
  Rectangle.prototype.constructor = Rectangle;

  var rect = new Rectangle();

  console.log('Is rect an instance of Rectangle?',
    rect instanceof Rectangle); // true
  console.log('Is rect an instance of Shape?',
    rect instanceof Shape); // true
  rect.move(1, 1); // Outputs, 'Shape moved.'

Javascript Bad Parts

  • Interfaz con el DOM
  • Insercion de punto y coma
  • comparaciones == != (ver tabla)
  • Es facil!

Ejercicios JS 1

Crear una funcion buscar(array, criterio, siVacio), que reciba un array y dos funciiones si criterio es verdadero devolver el elemento encontrado sino doveolver el resultado de ejecutar la otra funcion "siVacio"

buscar(
  [1,2,3,4],
  function(each){ return each === 5},
  function() {return 'no encontrado'}
);
                        

Ejercicios JS 2

Completar el ejemplo de Shape para que incluya circulos y triangulos, un metodo para mostrar cada uno por consola y un metodo para escalar cada figura en un porcentaje

Ejercicio JS 3

Crear mediante un objeto literal la estructura de 3 carreras de grado y una de pregrado de la utn con sus materias, duracion de cada carrera, nombre de la materia y descripción.

A partir de este arbol mostrar por consola codigo html que represente este arbol con un formato amigable (h1, h2 para titulos, ol, ul para listas, etc)

Angular Intro

  • TypeScript -> ES6
  • Modulos Ng and ES6
  • Componentes
  • Templates
  • Bootstraping
  • Data Binding
  • Directives
  • Services
  • HTTP

Angular Ventajas

  • TypeScript y ES6
  • Arquitectura de Componentes
  • Modularidad (ES6)
  • RxJs - Reactive Programming
  • Mejor Performance
  • Architectura un poco mas simple
  • CSS Modular
  • Testeabilidad
  • Una Arquitectura completa

Angular Contras

  • Usa TypeScript casi obligatoriamente
  • Cambio total con respecto a AngularJs
  • Framework Complejo
  • Una Arquitectura completa
  • Tiempo de Build lento
  • Syntaxis de los templates mas alejada de html

Arquitectura

Componentes

  • Permiten crear nuevos tags html
  • Se definen como clases con anotaciones
  • La instancia se vincula al template
  • Dependencias Explicitas
  • Hojas de Estilos modulares
  • Usa el Shadow Dom
  • Callbacks del ciclo de vida (hooks)


    
    
    

Ejemplos

Componentes de material design



  
    
Shiba Inu Dog Breed
Photo of a Shiba Inu

Estp es una descripcion del perro

Link

Sintaxis de un componente

import { Component } from '@angular/core';
import {TodoItem} from './todo-item'

@Component({
  selector: 'app-root',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.css']
})
export class AppComponent {
  title = 'app works!';
  list: Array<TodoItem>;
  item: TodoItem;

  constructor() {
    this.list = [];
    this.item = new TodoItem;
  }

  add( anItem: TodoItem) {
    this.list.push(anItem);
    this.item = new TodoItem;
  }

  remove( index: number) {
    this.list.splice(index,1);
  }
}

Templates


<h1 [ngClass]="{'big': list.length > 5}" >Todo {{list.length}}</h1>
  <input type="text" [(ngModel)]="item.text">
  <input type="button"  value="Add" (click)="add(item)">
  <ul>
    <li
    [ngClass]="{'done': todoItem.done}" \
    *ngFor="let todoItem of list; let i = index">
      <input type="checkbox" [(ngModel)]="todoItem.done">
        {{todoItem.text}}
        <input
          *ngIf="todoItem.done"
          (click)="remove(i)"
          type="button"
          name="name"
          value="X">
      </li>
  </ul>

Template Bindings

  • Expresiones ejecutables
  • Se reemplaza la expresion por su resultado
  • Se detectan automaticamente los cambios.

Resultado {{2+2}}

{{items.length === 0 ? 'Vacio' : items.length}} #Palabras: {{palabras.split(' ').length}}

{{Expresiones de un Template}}

  • sin efectos secundarios =, new, ++ ;
  • El scope es la instancia del componente
  • No hay espacio de nombres global
  • Rapido
  • Idempotente

[Binding de atributos]

  • Sintaxis: [atributo]="expresion"
  • Si se omiten los corchetes se pasa un string
  • El valor de resultado se remplaza.
  • Si el atributo no esta en el dom se usa [attr.atributo]
  • En nuestros componentes se pueden declarar atributos custom




                    

(Eventos)

  • Sintaxis: (evento)="expresion($event)"
  • Debe ser una expresion angular
  • Esta disponible la variable $event
  • estan disponibles todos los eventos del dom (click, focus, input, keyup, keydown, etc)
  • En nuestros componentes se pueden declarar eventos custom



Elementos de un template

  • Codigo HTML
  • bindings {{}}
  • attribute []
  • events (): Statements $event available
  • Two Way binding [()]
  • Directives
  • Components
  • Pipes

Directivas del framework

  • NgClass, NgStyle
  • *NgIf *NgFor *NgSwitch

Directiva *NgIf

Permite incluir o eliminar un elemento del DOM con una condición

No hay resultados en la lista
  • Elemento 1
  • Elemento 2

Directiva NgClass

Permite incluir o eliminar classes de un elemento dinamicamente

  X
  X
  X
  X
  X
  X

Clase Binding

<!-- toggle the "special" class on/off with a property -->
<div [class.special]="isSpecial">The class binding is special</div>

<!-- binding to `class.special` trumps the class attribute -->
<div class="special"
     [class.special]="!isSpecial">This one is not so special</div>

Estilo Binding

<button [style.color] = "isSpecial ? 'red': 'green'">Red</button>
<button [style.background-color]="canSave ? 'cyan': 'grey'" >Save</button>
<button [style.font-size.em]="isSpecial ? 3 : 1" >Big</button>
<button [style.font-size.%]="!isSpecial ? 150 : 50" >Small</button>

Bindings de Atributos, Clases y Estilos

<table border=1>
  <!--  expression calculates colspan=2 -->
  <tr><td [attr.colspan]="1 + 1">One-Two</td></tr>

  <!-- ERROR: There is no `colspan` property to set!
    <tr><td colspan="{{1 + 1}}">Three-Four</td></tr>
  -->

  <tr><td>Five</td><td>Six</td></tr>
</table>

Directiva *NgFor

Permite iterar por una coleccion y generar elementos DOM en cada iteracion

 
  • {{language}}

Ejemplo

Crear una Lista de tareas para hacer en en un unico componente Angular, usando las directivas del framework

La lista se compone de un input de texto con un boton de agregar y una lista de tareas donde cada una se puede eliminar tambien con un botón

Ademas puede setear cada tarea como completada/incompleta con un boton adicional en cada una

Ejemplo

Entrada y salida de un componente

import {Component} from '@angular/core';

@Component({
  selector: 'counter',
  template: `
    

Count: {{num}}

` }) export class CounterComponent { num = 0; increment() { this.num++; } }

Entrada y salida de un componente

import { Component, EventEmitter, Input, Output } from '@angular/core';

@Component({
  selector: 'counter',
  templateUrl: 'app/counter.component.html'
})
export class CounterComponent {
  @Input()  count = 0;
  @Output() result = new EventEmitter();

  increment() {
    this.count++;
    this.result.emit(this.count);
  }
}

Custom Two Way Binding


import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'counter',
  templateUrl: 'app/counter.component.html'
})
export class CounterComponent {
  @Input() count = 0;
  @Output() countChange = EventEmitter();

  increment() {
    this.count++;
    this.countChange.emit(this.count);
  }
}

Proyeccion de Contenido

import { Component } from '@angular/core';

@Component({
  selector: 'child',
  template: `
    

Child Component

` }) export class ChildComponent { }
 
    

Contenido proyectado.

Seleccion de contenido proyectado



  
Section Content
div with .class-select
Footer Content
Header Content

Seleccion de contenido proyectado

Child Component with Select

Ciclo de Vida

  • ngOnChanges - cuando cambia un binding input
  • ngOnInit - despues del primer ngOnChanges
  • ngDoCheck - despues de cada change detection
  • ngAfterContentInit - despues de inicializado el contenido

Ciclo de Vida

  • ngAfterContentChecked - despues de cada checkeo del contenido
  • ngAfterViewInit - despues de incializada la vista
  • ngAfterViewChecked - despues de cada check de la vista
  • ngOnDestroy - justo antes de destruir el componente

Ciclo de Vida mas importantes

  • ngOnInit - despues del primer ngOnChanges
  • ngOnChanges - cuando cambia un binding input
  • ngOnDestroy - justo antes de destruir el componente

Usando otros componentes

@ViewChild

import { Component, ViewChild } from '@angular/core';
import { AlertComponent } from './alert.component';

@Component({
    selector: 'app-root',
    template: `
    My alert
      `
})
export class AppComponent {
  @ViewChild(AlertComponent) alert: AlertComponent;

  showAlert() {
    this.alert.show();
  }
}

@ViewChildren

import { Component, QueryList, ViewChildren } from '@angular/core';
import { AlertComponent } from './alert.component';

@Component({
    selector: 'app-root',
    template: `
    
      Step 1: Learn angular
    
    
      Step 2: Love angular
    
    
      Step 3: Build app
    
      `
})
export class AppComponent {
  @ViewChildren(AlertComponent) alerts: QueryList;
  alertsArr = [];

  ngAfterViewInit() {
    this.alertsArr = this.alerts.toArray();
  }

  showAlert(step) {
    this.alertsArr[step - 1].show(); // step 1 is alert index 0
  }
}

Content Children

@ContentChild y @ContentChildren

trabajan de la misma forma que @ViewChild y @ViewChildren pero sobre el contenido proyectado

ElementRef

import { AfterContentInit, Component, ElementRef } from '@angular/core';

@Component({
    selector: 'app-root',
    template: `
    

My App

      {{ node }}
    
` }) export class AppComponent implements AfterContentInit { node: string; constructor(private elementRef: ElementRef) { } ngAfterContentInit() { const tmp = document.createElement('div'); const el = this.elementRef.nativeElement.cloneNode(true); tmp.appendChild(el); this.node = tmp.innerHTML; } }

Variables de Referencia de un template


<!-- phone refers to the input element; pass its `value` to an event handler -->
  <input #phone placeholder="phone number">
  <button (click)="callPhone(phone.value)">Call</button>

  <!-- fax refers to the input element; pass its `value` to an event handler -->
  <input ref-fax placeholder="fax number">
  <button (click)="callFax(fax.value)">Fax</button>

Modulos y NgModule

export class ZipCodeValidator implements StringValidator {
    isAcceptable(s: string) {
        return s.length === 5 && numberRegexp.test(s);
    }
}
import { ZipCodeValidator } from "./ZipCodeValidator";

let myValidator = new ZipCodeValidator();
 NgModule(options : {
constructor(options?: NgModuleMetadataType)
providers : Provider[]
declarations : Array<Type<any>|any[]>
imports : Array<Type<any>|ModuleWithProviders|any[]>
exports : Array<Type<any>|any[]>
entryComponents : Array<Type<any>|any[]>
bootstrap : Array<Type<any>|any[]>
schemas : Array<SchemaMetadata|any[]>
}) 

Subcomponentes Input and Output

import { Component, Input, Output, EventEmitter } from '@angular/core';

import {TodoItem} from './todo-item';

@Component({
    selector: 'todo-line',
    template: `
    <input type="checkbox" [(ngModel)]="item.done">
      {{item.text}}
      <input type="button" name="name" value="X" (click)="remove(item)">
`
})
export class TodoLine {
  @Input() item: TodoItem;
  @Output() removeRequested: EventEmitter<any> = new EventEmitter();

  remove( index: number) {
    this.removeRequested.emit(this.item)
  }
}

Bootstraping

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app.module';

platformBrowserDynamic().bootstrapModule(AppModule);

Servicios

Un servicio es un componente no visual en Angular

Sirven para comunicar componentes, reutilizar funcionalidad o guardar información

Angular provee un servicio de Inyeccion de Dependencias

Inyección de Dependencias

Inversion de Control

El componente no instancia sus dependencias, solo especifica lo que necesita

export class ProfileComponent {
    profileService = new ProfileService();
    }
export class ProfileComponent {
    constructor(private profileService: ProfileService) {}
    }

Inyeccion de dependencias

Angular lo tiene muy integrado

La estructura de modulos determina la inyeccion

En Angular se usa para principlamente para unit test

@Injectable()

import { Injectable } from '@angular/core';

@Injectable()
export class TodoService {
constructor() { }
}

Servicios que inyectan otros servicios

@Injectable()
export class ProfileService {
constructor(private httpClient: HttpClient) {}
}

export class ProfileComponent {
constructor(
private profileService: ProfileService,
private: BreakpointService) {}
}

Inyección de Dependencias: Modulo

@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
  ],
  providers: [TodoService],
  bootstrap: [AppComponent]
})
export class AppModule { }

Inyección de dependencias: root

import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class TodoService {
constructor() { }
}

Inyección de dependencias: componente

export class ProfileService {
constructor(private httpClient: HttpClient) {}
}
@Component({
/* . . . */
  providers: [ProfileServicet]
})
export class ProfileComponent {
constructor(
private profileService: ProfileService,
private: BreakpointService) {}
}

Servicios ¿Para que?

  • Agrupar funcionalidad compartida
  • Comunicar componentes
  • Ordenar mejor el código

Ejercicio

Tomar el codigo del branch servicios bajarlo a su proipio repo e implementar en otro servicio llamado LocalStorageService el guardado de la lista de forma persistente segun las apis del navegador

Que es la WWW

La World Wide Web (Web), es una red de recursos de información.

  1. Un esquema uniforme de nombres para localizar recursos en la Web (p.ej., URIs).
  2. Protocolos, para acceder a recursos con nombre en la Web (p.ej., HTTP).
  3. Hipertexto, para navegar fácilmente entre recursos (p.ej., HTML, DOM).

URI: Uniform Resource Identifier

scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]
Ejemplos
  • http://www.google.com
  • https://andres.otaduy:mypassword@frro.utn.edu.ar/~aotaduy/test.php
  • http://andres.otaduy:mypassword@frro.utn.edu.ar/~aotaduy/test.php
  • ftp://example.org/resource.txt
  • mailto:max@provider.com

HTTP

Hyper Text transfer protocol

Actualmente el protocolo por defecto para trabajar en el browser

Ultimamente el mas popular para intercambio de datos entre aplicaciones

Protocolo HTTP

http

Protocolo HTTP

  • Client - Server
  • Request - Response
  • Recursos a traves de URL's
  • Stateless: Sin estado
  • Conectionless: Sin conexion
  • Texto plano

HTTP Request

  • Verb
  • Header
  • Header Fields
  • Body (opcional)
  GET /u/1819149?s=64&v=4 HTTP/1.1
  Host: avatars0.githubusercontent.com
  Pragma: no-cache
  Cache-Control: no-cache
  User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3)
  Accept: image/webp,image/apng,image/*,*/*;q=0.8
  Referer: https://github.com/
  Accept-Encoding: gzip, deflate, br
  Accept-Language: en-US,en;q=0.9,es;q=0.8

HTTP Verbs

  • GET
  • POST
  • HEAD
  • PUT
  • DELETE
  • OPTIONS
  • ... Ver mas
Algunos metodos son idempotentes como DELETE, y PUT

HTTP Response

HTTP/1.1 200 OK
Accept-Ranges:bytes
Access-Control-Allow-Origin:*
Cache-Control:max-age=300
Connection:keep-alive
Content-Length:2581
Content-Type:image/jpeg
Date:Wed, 14 Mar 2018 20:03:17 GMT
Expires:Wed, 14 Mar 2018 20:08:17 GMT
Last-Modified:Mon, 26 Aug 2013 02:08:22 GMT
Timing-Allow-Origin:https://github.com

JPGBASE64BBFSD
  • Status line - Response Code
  • Response Headers
  • Body

HTTP Response codes

  • 1xx Informational responses.
  • 2xx Success. (200-OK, 201-Created)
  • 3xx Redirection. (301-Moved Permanently)
  • 4xx Client errors.(400 Bad request, 401)
  • 5xx Server errors. (501 Not implemented, 500)

Angular y HTTP

En la version 9 solo podemos usar el HttpClientModule

En versiones anteriores habia otra opción

El modulo usa observables para la notificación y manejo de ejecución asincrona

Observable

Basicamente es un objeto que mantiene una lista de suscriptores a los que les avisa cuando se produce un cambio

RxJS

Es una bibliototeca para el manejo de eventos, documentación

Angular y Http

En angular el cliente de http devuelve un observable que emite un evento cuando llega la respuesta

import { Injectable } from '@angular/core';

@Injectable()
export class TodoService {
  list = [];
  constructor(private: http: HttpClient) { }
  getList() {
    this.http.get('https://myapi.todo.com/v1/items')
        .subscribe(response => this.list = response.items)
                            }
  }

Angular y Http

También cuando hacemos un POST, tenemos que suscribirnos, sino no se hace la llamada

import { Injectable } from '@angular/core';

@Injectable()
export class TodoService {
  list = [];
  constructor(private: http: HttpClient) { }
  addItem() {
    this.http.post('https://myapi.todo.com/v1/item',
      {description: 'comprar yerba', isCompleted: false}
    ).subscribe(response => this.list = response.items)
  }
  }

Http Errores

Los observables incluyen varias formas de manejo de error, ya sea con una callback

import { Injectable } from '@angular/core';

@Injectable()
export class TodoService {
  list = [];
  constructor(private: http: HttpClient) { }
  getList() {
    this.http.get('https://myapi.todo.com/v1/items')
        .subscribe(
            response => this.list = response.items,
            error => this.error = error)
                            }
  }

Http Errores

Operadores como catchError

import { Injectable } from '@angular/core';

@Injectable()
export class TodoService {
  list = [];
  constructor(private: http: HttpClient) { }
  getList() {
    this.http.get('https://myapi.todo.com/v1/items')
        .pipe(catchError(error => {return []}))
        .subscribe(response => this.list = response.items)
    }
  }

Opciones por defecto

Por defecto se trabaja con json y se observa el body

options: {
    headers?: HttpHeaders | {[header: string]: string | string[]},
    observe?: 'body' | 'events' | 'response',
    params?: HttpParams|{[param: string]: string | string[]},
    reportProgress?: boolean,
    responseType?: 'arraybuffer'|'blob'|'json'|'text',
    withCredentials?: boolean,
  }

Headers

Para setear headers se utiliza el objeto options

const httpOptions = {
  headers: new HttpHeaders({
    'Content-Type':  'application/json',
    'Authorization': 'my-auth-token'
  })
};
   this.http.post(url, body, options).subscribe()
  }

Ejercicio

Vamos usar la api de realWorld app
  1. Usando la API de RealWorld hacer una nube de tags que me permita obtener los tags del sitio y al hacer click en uno de ellos me muestre los primeros 5 articulos completos de cada TAG. Mostrando titulo, descripcion body fecha de creacion y lista de tags asociada a ese articulo.
Codigo de base y ejemplo

Ejercicio 2

Con un usuario hardocdeado agregar un boton a un articulo del ejercicio anterior y hacer que le agregue un comentario ingresado en un textarea. tenemos que llamar el metodo login que nos va a devolver un objeto con un token. Ese token lo tenemos que agregar en un header del post para agregar el comentario con el siguiente formato

Authorization: Token [aca concatenado va el token obtenido]

Angular Routing

En una SPA no hace falta el ruteo entonces

Porque Routear

  • Estado de la App
  • Modularizar la App
  • Implementar restricciones de roles

Configuracion

Base URL Tag

Definicion de Ruta

Las rutas de una app estan en una array,

Con este array y el RouterModule se crean las rutas

const routes: Routes = [
      { path: 'component-one', component: ComponentOne },
      { path: 'component-two', component: ComponentTwo },
      { path: 'component-three', component: ComponentTwo },
      { path: '', redirectTo: 'component-one', pathMatch: 'full' },
    ];
    export const routing = RouterModule.forRoot(routes);
                        

Configuracion de Rutas

Despues se importa este modulo


...
import { routing } from './app.routes';

@NgModule({
  imports: [
    BrowserModule,
    routing
  ],
  declarations: [
    AppComponent,
    ComponentOne,
    ComponentTwo
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule {
}
                        

Links entre rutas

RouterLink

Component One

Navegando por programa

this.router.navigate(['/component-one']);

Parametros de Rutas


    export const routes: Routes = [
  { path: '', redirectTo: 'product-list', pathMatch: 'full' },
  { path: 'product-list', component: ProductList },
  { path: 'product-details/:id', component: ProductDetails }
];

goToProductDetails(id) {
  this.router.navigate(['/product-details', id]);
}

Parametros de Rutas


import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'product-details',
  template: `
    
Showing product details for product: {{id}}
`, }) export class LoanDetailsPage implements OnInit, OnDestroy { id: number; private sub: any; constructor(private route: ActivatedRoute) {} ngOnInit() { this.sub = this.route.params.subscribe(params => { this.id = +params['id']; // (+) converts string 'id' to a number // In a real app: dispatch action to load the details here. }); } ngOnDestroy() { this.sub.unsubscribe(); } }

Guards


import { Routes, RouterModule } from '@angular/router';
import { AccountPage } from './account-page';
import { LoginRouteGuard } from './login-route-guard';
import { SaveFormsGuard } from './save-forms-guard';

const routes: Routes = [
  { path: 'home', component: HomePage },
  {
    path: 'accounts',
    component: AccountPage,
    canActivate: [LoginRouteGuard],
    canDeactivate: [SaveFormsGuard]
  }
];

export const appRoutingProviders: any[] = [];

export const routing = RouterModule.forRoot(routes);
                        

CanActivate


import { CanDeactivate } from '@angular/router';
import { Injectable } from '@angular/core';
import { AccountPage } from './account-page';

@Injectable()
export class SaveFormsGuard implements CanDeactivate {

  canDeactivate(component: AccountPage) {
    return component.areFormsSaved();
  }

}
                        

Ejercicio

Realizar una aplicación que permita editar una lista de usuarios
La aplicación debe tener una pagina de login,
el usuario logueado es parte de esa lista.
La aplicación debe tener una pagina para mostrar la lista
Otra para editar los usuarios
Otra para dar de alta un usuario
Otra para ver un usuario.
(Se puede usar el formulario de edición con los campos en readonly)
Y otra para eliminar los usuarios

Cada usuario tiene permisos de
agregar nuevos usuarios,
editar usuarios
eliminar usuarios
editar sus propios datos

El usuario no puede eliminarse a si mismo
                        

Reactive Forms

Framework para manejar formularios y validaciones en angular

  • Inmutable
  • Streams Observables
  • Acceso sincrono
  • Validacion

Habilitar ReactiveForms

@NgModule({
  imports: [
    BrowserModule,
    ReactiveFormsModule
  ],
  declarations: [
    AppComponent,
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule {
}                       

Crear un form control


export class NameEditorComponent {
  name = new FormControl('');
}                        


                    

Modificar el valor


updateName() {
  this.name.setValue('Nancy');
}                        

Varios controles

export class ProfileEditorComponent {
  profileForm = new FormGroup({
    firstName: new FormControl(''),
    lastName: new FormControl(''),
  });
}                       

                            

Actualizacion parciales del modelo

updateProfile() {
  this.profileForm.patchValue({
    firstName: 'Nancy',
    address: {
      street: '123 Drew Street'
    }
  });
}             

Validaciones

  profileForm = new FormGroup({
    firstName: new FormControl('Andres', [Validators.required, Validators.maxlength(10)]),
    lastName: new FormControl('', Validators.required),
  });
}             

Validaciones Custom

 export function ValidateUrl(control: AbstractControl) {
  if (!control.value.startsWith('https') || !control.value.includes('.io')) {
    return { validUrl: true };
  }
  return null;
}
  profileForm = new FormGroup({
    url: new FormControl('Andres', [Validators.required, ValidateUrl]),
  });
}             

Ejercicio

Usando la rama reactive-forms del repositorio de ejemplos Modificar el ejemplo de Todolist para que permita:

  1. Usar reactive forms para el campo de la tarea
  2. Agregar un boton para editar la tarea
  3. Agregar un segundo campo de url a la tarea que valide que es un url valido