Il 0% ha trovato utile questo documento (0 voti)
1 visualizzazioni

Angular

Caricato da

scarselli.marika
Copyright
© © All Rights Reserved
Formati disponibili
Scarica in formato PDF, TXT o leggi online su Scribd
Il 0% ha trovato utile questo documento (0 voti)
1 visualizzazioni

Angular

Caricato da

scarselli.marika
Copyright
© © All Rights Reserved
Formati disponibili
Scarica in formato PDF, TXT o leggi online su Scribd
Sei sulla pagina 1/ 100

Filippo Bergamasco ( filippo.bergamasco@unive.

it)
http://www.dais.unive.it/~bergamasco/
DAIS - Università Ca’Foscari di Venezia
Anno accademico: 2017/2018
Angular (conosciuto anche come Angular 2 o
Angular 5) è un framework front-end per lo sviluppo
di client web SPA
● Sviluppato da Google (Angular team)
● Open-source
● Basato su TypeScript
● Modulare

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


Angular (versione 2) è stato riscritto quasi interamente
a partire dal vecchio framework web Angular 1.x. La
versione stabile è stata rilasciata il 14 Settembre 2016.

Angular 1.x continua ad esistere come progetto


indipendente con il nome AngularJS, ma è stato
ampiamente superato dal più moderno Angular

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


La versione 2, nota semplicemente come Angular, è
quella che ha portato il cambiamento radicale di:
● Architettura (components vs. scope/controllers)
● Linguaggio (scritta interamente in TypeScript)
● Struttura: classi, moduli, etc (grazie a typescript)

Le versioni successive, 4 e 5 sono retro compatibili


con Angular 2
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
La filosofia alla base dei due frameworks è
completamente diversa. JQuery fornisce strumenti
per la manipolazione diretta del DOM.
Angular è più focalizzato su una descrizione
dichiarativa dei dati che vengono associati ad
elementi del DOM attraverso un processo chiamato
data binding.
Angular è un framework complesso ma con
un’architettura pensata per lo sviluppo di SPA
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
Model-view-controller è un design pattern
tipicamente utilizzato per lo sviluppo di web
applications perché permette la “separation of
concerns” in cui il data model viene separato dalla
business e presentation logic

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


Client browser Web server Database

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


Web server

Database
Client browser

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


Web server

Database
Client browser

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


● Contiene i dati di un certo dominio
● Contiene la logica per creare, modificare e gestire i
dati
● Fornisce delle API che espongono i dati e le
operazioni su di essi
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
● NON deve esporre i dettagli su come i dati sono ottenuti o
gestiti (il web service non deve essere esposto al controller e
alle view)
● NON deve contenere la logica che trasforma i dati rispetto
alle interazioni con l’utente (compito del controller)
● NON deve contenere la logica di visualizzazione dei dati
(compito della view) F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
● Contiene la logica che permette di modificare il
modello sulla base dell’interazione con l’utente
● Contiene la logica per impostare lo stato iniziale della
view (template)
● Contiene le funzionalità richieste dal template per
accedere ai dati
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
● NON deve gestire la visualizzazione dei dati (non deve
modificare il DOM)
● NON deve gestire la logica di persistenza dei dati
(Compito del modello attraverso il web service)

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


● Contiene la logica di markup necessaria a presentare i
dati all’utente
● NON deve contenere alcuna logica di modifica,
creazione e gestione dei dati
● NON deve contenere la logica dell’applicazione
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
Angular utilizza i decoratori per “annotare” una classe, un
metodo o delle property e aggiungere metadati e
features aggiuntive a run-time.
Ad esempio un class decorator è applicato al
costruttore della classe e può essere usato per
osservare, modificare o sostituire la definizione della
classe
Un decorator può a sua volta dipendere da parametri
valutati a runtime
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}

@sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
I building-blocks di un’app Angular
sono chiamati NgModules che
fungono da contenitori di
componenti, service providers, e tutto
il codice accomunato da un certo
dominio, workflow o features
strettamente legate

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


Ogni app Angular ha almeno un
NgModule chiamato “root module”
(per convenzione è chiamato
AppModule) che definisce come
effettuare il “bootstrap”
dell’applicazione (quali componenti
“root” inserire all’interno della pagina)

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


● Un modulo può importare ed
esportare funzionalità da/verso
altri moduli
● Le librerie built-in di Angular sono
contenute in NgModules:
Ex:
HttpModule from @angular/http
FormsModule from @angular/core

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


Un modulo in Angular è definito attraverso una classe
decorata con il decorator @NgModule. Il decorator prende
come parametro un oggetto che descrive i metadati del
modulo:
● Declarations: le componenti, servizi, etc contenuti nel
modulo
● Exports: i componenti da esportare
● Imports: I moduli le cui classi esportate sono necessarie ai
componenti del modulo
● Providers: service providers esportati dal modulo

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

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

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


In Angular un modulo è una classe annotata con il
decorator @NgModule.
Il browser deve però conoscere dove si trova il codice
contenuto nei moduli importati. Per fare questo si
utilizzano i moduli Javascript (TypeScript) che
descrive l’associazione tra gli oggetti esportati e i files
in cui il codice è definito
import { BrowserModule } from '@angular/platform-browser';

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

@NgModule({ Javascript modules


imports: [ BrowserModule ],
providers: [ Logger ],
declarations: [ AppComponent ],
exports: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

@NgModule({
imports: [ BrowserModule ],
providers: [ Logger ],
declarations: [ AppComponent ],
exports: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { } Angular module

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


I components permettono di visualizzare i dati
dell’applicazione all’utente controllando una porzione
di schermo visibile (DOM) chiamata “view”.

Un Component è formato da una classe che contiene


i dati e la logica ad essi associati. Attraverso i metadati
definiti dal @Component decorator è associato ad un
template HTML che definisce la view da visualizzare

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


Template
Component Metadata
< >

View

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


<!doctype html>
<html lang="en">
<head><meta charset="utf-8"><title>Testng1</title>
<base href="/">
<meta name="viewport" content="width=device-width,
initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html> index.html
import { Component } from '@angular/core';

@Component({
selector: 'app-root',
<div style="text-align:center">
templateUrl: './app.component.html',
<h1>
styleUrls: ['./app.component.css']
Welcome to {{ title }}
})
</h1>
export class AppComponent {
</div>
title = 'app';
}
app.component.ts
app.component.html
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
Le views possono essere strutturate in una gerarchia
(una view può includere altre view). Una child-view
può appartenere allo stesso modulo o ad un altro
NgModule

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


Come sincronizzare i dati presenti nella gerarchia
dei componenti con il DOM dei template?
Attraverso il meccanismo del binding markup:
● Interpolation: Inserisce nel codice HTML la
stringa valutata in un espressione
● Property binding: Setta la proprietà di un view
element (DOM o Component) sulla base di un
espressione
● Event binding: Permette ad un componente di
rispondere ad un evento del DOM
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
import { Component } from '@angular/core'; <div style="text-align:center">
<h1>
@Component({ Welcome to {{ title }}
selector: 'app-root', </h1>
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'] <button [disabled]="isDisabled" >Cancel is {{
}) isDisabled ? "disabled" : "enabled" }}
</button>
export class AppComponent {
title = 'app'; <button (click)="disableToggle()" >
isDisabled = true; {{isDisabled ? "enable" : "disable"}} the
other button</button>
disableToggle() {
this.isDisabled = !this.isDisabled; </div>
}
}

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


import { Component } from '@angular/core'; <div style="text-align:center">
<h1>
@Component({ Welcome to {{ title }}
selector: 'app-root', </h1>
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'] <button [disabled]="isDisabled" >Cancel is {{
}) isDisabled ? "disabled" : "enabled" }}
</button>
Interpolation
export class AppComponent {
title = 'app'; <button (click)="disableToggle()" >
isDisabled = true; {{isDisabled ? "enable" : "disable"}} the
other button</button>
disableToggle() {
this.isDisabled = !this.isDisabled; </div>
}
}

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


import { Component } from '@angular/core'; <div style="text-align:center">
<h1>
@Component({ Welcome to {{ title }}
selector: 'app-root', </h1>
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'] <button [disabled]="isDisabled" >Cancel is {{
}) isDisabled ? "disabled" : "enabled" }}
</button>
Property
export class AppComponent {
binding
title = 'app'; <button (click)="disableToggle()" >
isDisabled = true; {{isDisabled ? "enable" : "disable"}} the
other button</button>
disableToggle() {
this.isDisabled = !this.isDisabled; </div>
}
}

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


import { Component } from '@angular/core'; <div style="text-align:center">
<h1>
@Component({ Welcome to {{ title }}
selector: 'app-root', </h1>
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'] <button [disabled]="isDisabled" >Cancel is {{
}) isDisabled ? "disabled" : "enabled" }}
</button>
Event
export class AppComponent {
binding
title = 'app'; <button (click)="disableToggle()" >
isDisabled = true; {{isDisabled ? "Enable" : "Disable"}} the
other button</button>
disableToggle() {
this.isDisabled = !this.isDisabled; </div>
}
}

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


1. Le espressioni, utilizzate nell’interpolation e nel
property binding devono essere Idempotenti:
devono poter essere valutate più volte senza
cambiare lo stato dell’applicazione

Ex: {{ counter = counter + 1 }} non è idempotente e


genera pertanto un errore

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


2. Non è possibile accedere ad oggetti che si trovano
al di fuori del contesto definito dal componente del
template. Ciascun metodo o proprietà è soltanto
quella definita all’interno del componente

Ex: {{ Math.floor(0.1) }} non è un espressione valida


perchè Math fa parte dell’oggetto globale e non è in
generale una property del component
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
Nei casi in cui la proprietà mappata è una stringa è
possibile utilizzare alternativamente interpolazione o
property binding (il risultato è lo stesso)
<p><img src="{{heroImageUrl}}"> is the <i>interpolated</i> image.</p>
<p><img [src]="heroImageUrl"> is the <i>property bound</i> image.</p>

Attenzione: il binding si riferisce ad una proprietà del


DOM e non ad un attributo HTML (anche se spesso il
mapping è 1:1. Possiamo ad esempio scrivere:
<span [innerHTML]="title"></span>
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
Sono classi speciali che permettono di trasformare dati in
input in un output desiderato. Possono essere usati nel
data interpolation con la sintassi:
{{ expression | pipe:parameter }}
Esistono molteplici pipe built-in come date, lowercase,
uppercase, async, etc.
Possono essere creati custom implementando la classe
PipeTransform annotata con il decorator @Pipe

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


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

@Component({
selector: 'app-hero-birthday',
template: `<p>The hero's birthday is {{ birthday | date }}</p>`
})

export class HeroBirthdayComponent {


birthday = new Date(1988, 3, 15);
}

// Stampa April 15, 1988


//Invece di Fri Apr 15 1988 00:00:00 GMT-0700 (Pacific Daylight Time)

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


import { Pipe, PipeTransform } from '@angular/core'; import { Component } from '@angular/core';
/*
* Raise the value exponentially @Component({
* Takes an exponent argument that defaults to 1. selector: 'app-power-booster',
* Usage: template: `
* value | exponentialStrength:exponent <h2>Power Booster</h2>
* Example: <p>Super power boost: {{2 |
* {{ 2 | exponentialStrength:10 }} exponentialStrength: 10}}</p>
* formats to: 1024 `
*/ })
@Pipe({name: 'exponentialStrength'}) export class PowerBoosterComponent { }
export class ExponentialStrengthPipe implements
PipeTransform {
transform(value: number, exponent: string): number
{
let exp = parseFloat(exponent);
return Math.pow(value, isNaN(exp) ? 1 : exp);
}
}

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


Le direttive sono dei costrutti speciali applicabili agli
elementi HTML dei template che permettono di:
● Includere contenuti selettivamente
● Selezionare tra frammenti di contenuto
● Ripetere un certo frammento per ogni elemento di
un array
● etc.

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


La direttiva *ngIf include un elemento e il suo
contenuto soltanto se l’espressione valutata come
true

<div *ngIf="expr"></div>

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


La direttiva permette di scegliere diversi frammenti da
includere nel documento HTML sulla base del valore
dell’espressione
<div [ngSwitch]="num_products()">
<span *ngSwitchCase="1">Only one element</span>
<span *ngSwitchCase="2">Only two elements</span>
<span *ngSwitchDefault>More than 2 elements</span>
</div>
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
La direttiva permette di ripetere un certo frammento
più volte, iterando su una certa collezione

<ul>
<li *ngFor="let product of get_products()">
{{ product }}
</li>
<ul>

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


E’ possibile creare una variabile come riferimento ad
un elemento del DOM all’interno di un certo template

<input #phone placeholder="phone number">

<!-- lots of other elements -->

<!-- phone refers to the input element;


pass its `value` to an event handler -->
<button (click)="callPhone(phone.value)">Call</button>

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


Direttive speciali possono cambiare il tipo dell’oggetto
puntato dalla reference variable

<input type="text" #username="ngModel" required name="username" [(ngModel)]="user.username"


class="form-control" id="inputUsername" placeholder="Enter username">

<div [hidden]="username.valid || username.pristine"


class="alert alert-danger">
Username is required
</div>
Valid e pristine sono proprietà di ngModel e non
di HTMLFormElement.

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


Prima applicazione d’esempio

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


Angular-cli è un’utility che permette in modo
semplice:
● Di generare la struttura di una nuova app
● Di generare e aggiungere components, services e
moduli
● Di lanciare l’applicazione da un webserver locale,
aggiornando dinamicamente il codice quando i file
sorgente subiscono qualche modifica
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
$ ng new <app_name>
$ ls <app_name>
README.md karma.conf.js
package-lock.json protractor.conf.js tsconfig.json
e2e node_modules package.json
src tslint.json

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


$ ng generate component comp-name

create src/app/comp-name/comp-name.component.css (0 bytes)


create src/app/comp-name/comp-name.component.html (28 bytes)
create src/app/comp-name/comp-name.component.spec.ts (643 bytes)
create src/app/comp-name/comp-name.component.ts (280 bytes)
update src/app/app.module.ts (486 bytes)

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


$ ng serve --open

** NG Live Development Server is listening on localhost:4200, open your browser on


http://localhost:4200/ **
15% building modules 45/48 modules 3 active ...form-browser/esm5/platform-browser.jswebpack: wait until
bundle finished: /
Date: 2018-03-30T20:12:14.820Z
Hash: f8743c5298a26dc080f5
Time: 6669ms
chunk {inline} inline.bundle.js (inline) 3.85 kB [entry] [rendered]
chunk {main} main.bundle.js (main) 32 kB [initial] [rendered]
chunk {polyfills} polyfills.bundle.js (polyfills) 549 kB [initial] [rendered]
chunk {styles} styles.bundle.js (styles) 41.5 kB [initial] [rendered]
chunk {vendor} vendor.bundle.js (vendor) 7.42 MB [initial] [rendered]

webpack: Compiled successfully.

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


Concetti avanzati

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


DI è un pattern molto importante, utilizzato in molti
contesti nel framework Angular per gestire le
dipendenze fra componenti

Scopo: fare in modo che una classe riceva le


dipendenze di cui ha bisogno da una sorgente esterna
invece di istanziarle autonomamente

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


export class Car { La classe Car per funzionare ha
public engine: Engine; bisogno di Engine e Tires
public tires: Tires;
● Car istanzia un oggetto
constructor() { Engine e un oggetto Tires
this.engine = new Engine();
this.tires = new Tires(); nel proprio costruttore
}
● La classe Car è “fragile”
// Method using the engine and tires
drive() { perché dipende fortemente
return `${this.description} car with `
+ non solo dall’interfaccia delle
`${this.engine.cylinders} cylinders
and ${this.tires.make} tires.`;
dipendenze ma anche dalla
}
}
loro implementazione
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
● Cosa succede se la classe
Engine viene modificata in
export class Car { modo che il costruttore
public engine: Engine;
public tires: Tires;
richieda dei parametri?
● Cosa succede se in futuro
constructor() {
this.engine = new Engine(); viene creata una sottoclasse
this.tires = new Tires();
} di Tires che vorremmo usare
// Method using the engine and tires
drive() {
nella nostra classe Car?
return `${this.description} car with ` +
`${this.engine.cylinders} cylinders and
● Cosa succede se volessimo
${this.tires.make} tires.`;
} aggiungere a Car una classe
}
(servizio) condiviso con altre
istanze di Car?
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
● Testare la classe Car è
export class Car { complesso perché dobbiamo
public engine: Engine;
public tires: Tires;
essere in grado di istanziare
tutte le sue dipendenze
constructor() {
this.engine = new Engine();
this.tires = new Tires();

}

// Method using the engine and tires


drive() {
return `${this.description} car with ` +
`${this.engine.cylinders} cylinders and
${this.tires.make} tires.`;
}
}

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


Nel pattern DI la definizione delle
export class Car {
dipendenze è specificata nel
costruttore.
constructor(public engine: Engine, public
tires: Tires) {}
● La classe Car si aspetta di
ricevere le dipendenze di cui
// Method using the engine and tires
drive() { ha bisogno dall’esterno già
return `${this.description} car with `
+ pronte per l’uso
`${this.engine.cylinders} cylinders
and ${this.tires.make} tires.`;
● La classe Car non istanzia più
}
}
automaticamente le sue
dipendenze che vengono
“iniettate” dall’esterno
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
// Simple car with 4 cylinders let car
= new Car(new Engine(), new Tires());
● La classe Car è ora
dipendente soltanto
dall’interfaccia degli oggetti
class Engine2 {
constructor(public cylinders:
che utilizza
number) {}
}
● Il problema di istanziare gli
oggetti è stato spostato dal
// Super car with 12 cylinders
produttore al consumatore
let bigCylinders = 12;
let car = new Car(new dell’oggetto Car
Engine2(bigCylinders), new Tires());

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


Per far si che anche il consumatore non sia costretto a
gestire manualmente la creazione delle dipendenze,
un framework di DI fornisce in genere un componente
chiamato injector che:
● Gestisce una lista di oggetti “iniettabili” come
dipendenze
● Conosce come costruire ciascun oggetto
● E’ in grado di istanziare ciascun oggetto iniettabile
on-demand
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
In Angular un servizio (Service) è una classe che può
essere iniettata all’interno di un Component per
fornire funzionalità specifiche solitamente
indipendenti dalla logica di visualizzazione
Ex:
● Un component mostra la lista di utenti
● Un servizio permette di recuperare la lista da un
RESTful webservice
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
Un componente solitamente non implementa le
funzionalità che esulano dalla gestione della view
come:
● Gestire il recupero e invio di dati ad un server
● Validare l’input degli utenti
● Logging sulla console
● etc.
Utilizza invece dei servizi che possono essere
riutilizzati in più componenti
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
Un servizio è una qualsiasi classe decorata con il
decorator @Injectable.
● Un componente può utilizzare un servizio
definendolo come dipendenza all’interno del suo
costruttore
● Durante il processo di bootstrap, Angular crea un
Injector per iniettare i servizi all’interno dei
componenti

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


● L’injector di Angular mantiene in memoria tutte le
dipendenze già istanziate per poterle riutilizzare se
più componenti necessitano dello stesso servizio
(L’injector è di fatto una factory di dipendenze)
Come fa l’injector a sapere come creare le
dipendenze?
● Utilizzando un provider

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


Per ogni dipendenza necessaria all’interno di
un’applicazione, occorre registrare un provider
all’interno dell’injector di modo che possa creare
nuove istanze della dipendenza ogni volta che queste
sono richieste da un componente.
Per i servizi, il provider è semplicemente la classe
stessa. E’ possibile creare provider custom per i casi
particolari
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
Creiamo un servizio:

$ ng generate service logger

create src/app/logger.service.spec.ts (374 bytes)


create src/app/logger.service.ts (112 bytes)

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


//logger.service.ts
import { Injectable } from '@angular/core'; Il servizio è
@Injectable()
semplicemente una
export class LoggerService { classe con
constructor() { }
l’annotazione
@Injectable.
public log( message: string ) {
console.log( `${new Date()}: ${message}` ); Un servizio può
}
dipendere a sua volta
} da altri servizi

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


import { Component } from '@angular/core';
import { LoggerService } from './logger.service';
Il Componente che vuole
@Component({
selector: 'app-root', utilizzare il servizio inserisce la
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
sua dichiarazione nel
})
export class AppComponent {
costruttore.
products: {id: number, name: string}[] = []; E’ fondamentale annotare il
private idgen = new UniqueId();
parametro con il tipo corretto
constructor( private log: LoggerService ) {}
che sarà la chiave usata
add_product( p: string ) {
this.log.log('Adding new product');
dall’injector per capire che
}
this.products.push( {id: this.idgen.get(), name: p } );
istanza iniettare
delete_product( id: number ) {
this.log.log( 'Deleting product with id ' + id );
/*....*/ }
}
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
import { Component } from '@angular/core';
import { LoggerService } from './logger.service';
Per rendere il servizio
@Component({
selector: 'app-root', disponibile soltanto al
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
componente, possiamo
providers: [
{provide: LoggerService, useClass: LoggerService }
registrare il provider nella lista
],
})
di providers utilizzata dal
export class AppComponent { componente.
products: {id: number, name: string}[] = [];
private idgen = new UniqueId();
Questi sono definibili nei
constructor( private log: LoggerService ) {}
add_product( p: string ) {
metadati del componente
this.log.log('Adding new product');
this.products.push( {id: this.idgen.get(), name: p } );
stesso
}
delete_product( id: number ) {
this.log.log( 'Deleting product with id ' + id );
/*....*/ }
}
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
import { Component } from '@angular/core';
import { LoggerService } from './logger.service';
Chiave
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
providers: [
Classe da istanziare. Può
],
{provide: LoggerService, useClass: LoggerService }
essere diversa dalla chiave
})
export class AppComponent {
purché implementi la stessa
interfaccia
products: {id: number, name: string}[] = [];
private idgen = new UniqueId();
constructor( private log: LoggerService ) {}
add_product( p: string ) {
this.log.log('Adding new product');
this.products.push( {id: this.idgen.get(), name: p } );
}
delete_product( id: number ) {
this.log.log( 'Deleting product with id ' + id );
/*....*/ }
}
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
import { BrowserModule } from '@angular/platform-browser'; Per rendere il servizio
import { NgModule } from '@angular/core';
disponibile in tutta
import { AppComponent } from './app.component'; l’applicazione, lo stesso
import { LoggerService } from './logger.service';
provider può essere registrato
@NgModule({
direttamente nella definizione
declarations: [
AppComponent
del modulo root
],

In questo modo sarà usata


imports: [
BrowserModule
],
providers: [
una sola istanza di
],
{provide: LoggerService, useClass: LoggerService }
LoggerService per tutti i
bootstrap: [AppComponent]
})
componenti del modulo
export class AppModule { }

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


Esistono in realtà molteplici injectors, uno per ogni
componente presente nell’applicazione.
La gerarchia degli injectors è identica alla gerarchia
dei componenti

Le dipendenze vengono iniettate rispettando l’ordine


gerarchico tra i componenti
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
Quando un componente richiede una dipendenza,
Angular cerca di risolverla usando il provider
registrato sull’injector del componente stesso
Se l’injector del componente non contiene il provider,
la richiesta è delegata all’injector del componente
padre, risalendo la gerarchia dal basso verso l’alto
Se nemmeno l’injector del modulo contiene il provider
giusto, viene generato un errore
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
Un vantaggio della DI gerarchica è che possiamo
fornire servizi con diverso grado di specializzazione in
diversi punti della gerarchia

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


Un altro pattern molto importante utilizzato da
Angular è chiamato “Observer” ed è utilizzato per
passare messaggi tra due entità, chiamate
rispettivamente publisher e subscriber.

Sono fondamentali per permettere la gestione


asincrona dei dati risolvendo il problema in modo
dichiarativo
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
Un Observable definisce una funzione che può
pubblicare valori ad un certo observer.
A differenza delle promise, la funzione non viene
eseguita fintanto che l’entità che vuole consumare
quei valori non si “iscrive” a quel determinato
Observable.
Il consumer riceve notifiche sui valori pubblicati fino a
quando la funzione Observer ritorna oppure il
consumer si disiscrive (unsuscribe) esplicitamente
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
const sequenceObservable = new Observable( (observer) => {
const seq = [1, 2, 3];
let timeoutId;

// Will run through an array of numbers, emitting one value


// per second until it gets to the end of the array.
function doSequence(arr, idx) {
timeoutId = setTimeout(() => {
observer.next(arr[idx]);
if (idx === arr.length - 1) { Il costruttore di Observable
observer.complete();
} else { prende in input la funzione
doSequence(arr, idx++);
}
che pubblica valori ad un
}, 1000); certo observer
}

doSequence(seq, 0);

// Unsubscribe should clear the timeout to stop execution


return {unsubscribe() {
clearTimeout(timeoutId);
}};
});

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


const sequenceObservable = new Observable( (observer) => {
const seq = [1, 2, 3];
let timeoutId;

// Will run through an array of numbers, emitting one value


// per second until it gets to the end of the array.
function doSequence(arr, idx) {
timeoutId = setTimeout(() => {
observer.next(arr[idx]); Observer è un oggetto che
if (idx === arr.length - 1) { contiene 3 funzioni:
observer.complete();
} else { ● next( v ) è l’handler che
doSequence(arr, idx++); gestisce ciascun valore v che
}
}, 1000);
viene generato
} ● error( e ) è l’handler che
gestisce eventuali errori
doSequence(seq, 0);
● complete() [opzionale] è
// Unsubscribe should clear the timeout to stop execution l’handler che viene invocato
return {unsubscribe() { quando non ci sono più valori
clearTimeout(timeoutId);
}}; da pubblicare
});

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


Creare un Observable non comporta l’esecuzione
della funzione. Per iniziare a ricevere i valori è
necessario invocare il metodo subscribe passando
come parametri 3 funzioni: next, error e complete:

sequenceObservable.subscribe(
(x) => { console.log(“New value generated: “+x); },
(err) => { console.log(“Error: “ + err );
() => { console.log(“No more values”);
);

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


● Gli Observables sono dichiarativi, permettono di
creare delle “ricette” che possono essere eseguite
quando un componente invoca subscribe. Le
promise vengono invece eseguite appena
vengono create.
● Gli observable pubblicano una sequenza anche
infinita di valori. Le promise forniscono un solo
valore
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
● Gli observer possono creare delle catene di
trasformazione sui valori, che vengono eseguiti
solo quando un consumer invoca subscribe:

● Un observable può essere cancellato mentre è in


esecuzione invocando il metodo unsubscribe()

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


Reactive Extensions for JavaScript è una libreria
utilizzata da Angular che utilizza gli observables per
rendere più semplice lo sviluppo di codice asincrono
basato su callback.
Fornisce l’implementazione della classe Observable e
delle utility per creare Observables da altri tipi (come
array o promise) e modificare lo stream di valori
prodotti
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
Ex.1 Creare un observable da una sequenza di valori:
const nums = Observable.of(1, 2, 3);

Ex.2 Creare un nuovo observable che modifica al volo


i valori prodotti dall’observable precedente
const squareValues = nums.map((val: number) => val * val);
squaredNums.subscribe(x => console.log(x));

// Logs
// 1 4 9

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


L’operatore pipe permette di combinare più funzioni in
una singola funzione che le esegue in sequenza per
ogni valore generato
import { filter } from 'rxjs/operators/filter';
import { map } from 'rxjs/operators/map';

const squareOdd = Observable.of(1, 2, 3, 4, 5)


.pipe(
filter(n => n % 2 !== 0),
map(n => n * n)
);

// Subscribe to get values


squareOdd.subscribe(x => console.log(x)); // 4 16
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
E’ una classe fornita da Angular che implementa un
Observer aggiungendo la funzione emit(v). Quando
invocata, l’observer pubblica il valore v al consumer.
@Component({
selector: 'zippy',
template: `<div class="zippy">
<div (click)="toggle()">Toggle</div>
<div [hidden]="!visible">
<ng-content></ng-content>
</div> </div>`})
export class ZippyComponent {
visible = true;
public visibility_change = new EventEmitter<any>();

toggle() {
this.visible = !this.visible; if (this.visible) { this.visibility_change.emit(true); }
else { this.visibility_change.emit(false); }
}
} F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
E una delle classi più utilizzata di Angular.
Permette di effettuare chiamate HTTP asincrone e di
ritornare i valori ottenuti sotto forma di observable.

Observable<Users[]> o = http.get<Users[]>(‘http://myserver.com/api/v1/users)
.pipe(
tap(users => this.log(`fetched users: ‘+JSON.stringify(users) )),
catchError(this.handleError('getHeroes', []))
);

o.subscribe( (users)=>{ this.users = users } );

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


I metodi get e delete si aspettano di default dati
formattati come stringa JSON.
E’ possibile specificare headers aggiuntivi, parametri
della richiesta, etc.

Nota: La richiesta HTTP ritorna un observable.


Pertanto, la richiesta viene effettuata soltanto quando
è invocato il metodo subscribe()
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
Utilizzo:
1. Iniettare il servizio HttpClient nel proprio
componente o servizio
constructor( private http: HttpClient ) {}

2. Effettuare la richiesta
http.get( <url>:string, <options>:{} ) : Observable< any >

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


Eventuali headers
options: { aggiuntivi come
headers?: HttpHeaders | {
[header: string]: string | string[]; coppie chiave: valore
};
observe?: HttpObserve;
params?: HttpParams | {
[param: string]: string | string[];
};
reportProgress?: boolean;
responseType?: 'arraybuffer' | 'blob' | 'json' |
'text';
withCredentials?: boolean;
}

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


Se impostato,
options: { l’observable ritorna la
headers?: HttpHeaders | {
[header: string]: string | string[]; response completa
}; (con headers, valore
observe?: HttpObserve;
params?: HttpParams | { di ritorno, etc) e non
[param: string]: string | string[]; soltanto i dati
};
reportProgress?: boolean;
responseType?: 'arraybuffer' | 'blob' | 'json' |
'text';
withCredentials?: boolean;
}

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


options: {
headers?: HttpHeaders | {
[header: string]: string | string[]; Parametri della
}; request che vengono
observe?: HttpObserve;
params?: HttpParams | { automaticamente
[param: string]: string | string[]; serializzati secondo il
};
reportProgress?: boolean; tipo MIME
responseType?: 'arraybuffer' | 'blob' | 'json' | application/x-www-f
'text';
withCredentials?: boolean; orm-urlencoded
}

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


I metodi post() e put() di HttpClient permettono
l’invio di dati al server. La funzione supporta l’invio di
dati generici (oggetti) che vengono solitamente
serializzati in stringhe JSON
http.post( <url>:string, <body>: any, <options>:{} ) :
Observable< any >

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


Angular fornisce un componente speciale chiamato
Router che implementa una navigazione tra diverse
view con un modello analogo a quello comunemente
utilizzato dal browser:
● L’URL indica la view visualizzata
● Cliccando su un link la view viene sostituita con
quella puntata da un link
● I pulsanti back e forward permettono di navigare
avanti e indietro nella storia delle view visitate
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
Il modello di navigazione implementato dal router è
simile a quello del browser ma il tipo di applicazione
web resta di tipo single page:
● La modifica della view corrente non
necessariamente causa una chiamata HTTP
● Può essere modificata soltanto una porzione della
pagina corrente (una view), non tutta la pagina
● Gli eventi di routing possono essere intercettati e
scatenare altre azioni
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
Il primo step è specificare qual’è l’URL di base da cui il
router può comporre gli URL per tutte le sotto-view.
L’URL di base è impostato nel file index.html con il tag
<base>:

<base href="/">

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


Si genera poi un modulo e lo si aggiunge al progetto:

$ ng generate module app-routing --flat --module=app

Nome modulo
Importa automaticamente il
nuovo modulo nel modulo
Non generare una dir “app” (solitamente è il nome
dedicata per il modulo del root module)
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
Si specificano le route istanziando un oggetto Routes
https://angular.io/api/router/Routes
const routes: Routes = [
{ path: '', redirectTo: '/login', pathMatch: 'full' },
{ path: 'login', component: UserLoginComponent },
{ path: 'messages', component: MessageListComponent }
];

@NgModule({
imports: [ RouterModule.forRoot(routes) ],
exports: [ RouterModule ]
})
export class AppRoutingModule { }
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
Si invoca il metodo forRoot() che ritorna un modulo con tutti i servizi
e direttive necessarie per il suo utilizzo. Il modulo è configurato con
le route appena definite ed esportato agli altri componenti
dell’applicazione

const routes: Routes = [


{ path: '', redirectTo: '/login', pathMatch: 'full' },{ path: 'login', component:
UserLoginComponent },{ path: 'messages', component: MessageListComponent }
];
@NgModule({
imports: [ RouterModule.forRoot(routes) ],
exports: [ RouterModule ]
})
export class AppRoutingModule { }
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142
A questo punto è possibile inserire la direttiva
<router-outlet> nel template di un qualsiasi component o
direttamente in index.html. L’elemento specifica in quale
posto del DOM vanno inserite le varie view gestite dal router

<h1>{{title}}</h1>
<router-outlet></router-outlet>
<app-messages></app-messages>

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


E’ possibile cambiare view inserendo dei link all’interno del
codice HTML, come si farebbe con le normali pagine:

<a routerLink="/signup">Sign-up new user</a>

oppure programmaticamente nel codice di un component

constructor( private router: Router ) { }


change_page( ) {
this.router.navigate(['/page']);
}

F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142


Documentazione ufficiale:
https://angular.io/docs

Framework API list


https://angular.io/api
Cheat sheet
https://angular.io/guide/cheatsheet
F. Bergamasco - WEB APPLICATIONS AND TECHNOLOGIES - CT0142

Potrebbero piacerti anche