3 Direttive *ngFor, Servizi, Dependency Injection
Quando creiamo una classe nel componente, le variabili che aggiungiamo nella classe saranno accessibili anche nel template
export class UsersComponent { title = "Users"; }
a questo punto possiamo richiamare la proprietà title direttamente nell’HTML con le doppie graffe ( { { } } )
template: ` <table> <caption>{{title}}</caption> </table> `,
Se nella classe definissimo per esempio un array users, contenete i dati di un utente
export class UsersComponent { users = [{ nome: 'fag', cognome: 'pingwie', cf: 'fgapng44a21f221c', mail: 'pippo@tin.it', tel: '123456', prov: 'SV', eta: 55 } ];
potremmo ciclare i dati con angular mediante la direttiva *ngFor che corrisponde al costrutto for of di javascript
<tr *ngFor="let use of users">
definiamo una variabile con il let seguita dalla keyword of e il nome della collezione di dati da iterare.
A questo punto basterà specificare nei vari <td> le proprietà da stampare
<td>{{use.nome}}</td> <td>{{use.cognome}}</td> <td>{{use.cf}}</td> <td>{{use.mail}}</td> <td>{{use.eta}}</td> <td>{{use.tel}}</td> <td>{{use.prov}}</td>
Ovviamente questo era solo un esempio didattico, in realtà i dati non devono essere contenuti nel componente.
Servizio
Separiamo i dati dal componente creando un servizio e quindi nella stessa cartella del componente creiamo un nuovo file typescript con lo stesso nome al singolare, seguito dalla parola service
user.service.ts
creiamo la classe con lo stesso nome e l’ iniziale maiuscola e la parola Service
export class UserService { }
aggiungiamo un metodo getUsers() che conterrà il nostro array di dati; in seguito i dati potrebbero eventualmente essere recuperati da un server remoto, anzichè localmente da un array
export class UserService { getUsers() { return [ { nome: 'fag', cognome: 'pingwie', cf: 'fgapng44a21f221c', mail: 'pippo@tin.it', tel: '123456', prov: 'SV', eta: 55 }] } } }
adesso dobbiamo importare la classe nel nostro componente users.component.ts
import { UserService } from './user.service';
e inseriamo nella classe un costruttore che recuperi i dati dal servizio tramite una nuova istanza della classe UsersService e li inserisca nel nuovo array users[], creato per l’occasione, con il metodo getUsers() del servizio
export class UsersComponent { users = []; constructor() { const service = new UserService(); this.users = service.getUsers(); } }
Questo approccio ha delle lacune, in quanto se passassi dei parametri tipo un url nel metodo getUsers(url: string), dovrei passare tale parametro anche nell’stanza della classe ( const service = new UserService(‘https://bloccoappunti.it’); ) e dovrei andare a cambiare tutte le chiamate di questi metodi e classi creati nell’app.
Quindi l’errore logico da non commettere è quello di istanziare una classe e quindi creare un nuovo oggetto, dentro a un costruttore
Dependency injection
Con questa tecnica andrò ad iniettare il mio servizio di tipo UserService direttamente nel costruttore ed angular andrà a risolvere in automatico le dipendenze di questa classe creando le dovute istanze
constructor(service: UserService) { this.users = service.getUsers(); }
Provider
Visto che UserService sarà utilizzato come dependency injection, angular non sa dove reperire il servizio, quindi sarà necessario specificarlo come fornitore (provider).
Questo può essere fatto in 3 modi a seconda di dove vogliamo fruire del nostro servizio.
- Iniettarlo solo in un componente
Nelle proprietà del componente aggiungere la proprietà providers
@Component({ selector: 'app-general-layout', providers: 'users',
- Iniettarlo in un modulo
nella proprietà providers in app.module.ts o altro modulo personalizzato
@NgModule({ declarations: [ ...], providers: [UserService],
Se l’ide non lo aggiunge automaticamente sarà da importare il servizio anche nell’ app.module.ts
import { UserService } from './users/user.service';
- Iniettarlo globalmente in tutta l’app
In questo caso si userà la proprietà providedIn direttamente nel servizio e dicendogli che lo si uole disponibele per tutta l’app quindi la root
provideIn: 'root'
In ogni caso dobbiamo far si che angular risolva le dipendenze del servizio, e fargli capire che il servizio verrà iniettato, per far ciò dobbiamo inserire nel servizio un decoratore @Injectable(). Con @Ingectable() angular capisce che questo service può avere delle dipendenze e verrà utilizzato nella creazione di un singleton (unica istanza in un oggetto),
ecco tutto il servizio
import {Injectable} from '@angular/core'; @Injectable() export class UserService { getUsers() { return [ { nome: 'fag', cognome: 'pingwie', cf: 'fgapng44a21f221c', mail: 'pippo@tin.it', tel: '123456', prov: 'SV', eta: 55 } ]; } }
Oninit
Angular possiede dei ganci (hook) che permettono di capire quando un componente viene inizializzato.
Nella fattispecie l’interfaccia OnInit è responsabile di avviare il suo metodo ngOnInit() che eseguirà il codice all’interno di essa quando il componente viene caricato e solo una volta.
Aggiungiamo l’interfaccia nella classe UsersComponents con implements OnInit
export class UsersComponent implements OnInit {
a questo punto se l’ide lo prevede verrà aggiunta l’importazione di OnInit dal core di angular, altrimenti aggiungetela manualmente con import
import {Component, OnInit} from '@angular/core';
Ora non ci resta che aggiungere il metodo ngOnInit() nella classe, inserendo il servizio al suo interno
ngOnInit() { this.users = service.getUsers(); }
Il costruttore riceverà solo i parametri della dependency injection e creerà automaticamente la variabile service di tipo UserService iniettando la dipendenza.
Dovremo poi specificare la visibilità di service, nel nostro caso ad uso privato con private. A questo punto in ngOnInit() dobbiamo aggiungere il this a service perchè altrimenti il metodo non conoscerebbe la variabile locale sevice
export class UsersComponent implements OnInit { users = []; constructor( private service: UserService) { } ngOnInit() { this.users = this.service.getUsers(); } }
Questo codice è una forma più compatta, ma avrei potuto dichiarare la variabile privata, iniettare il servizio, dire che questa variabile riceve il valore dal servizio e nel metodo ngOnInit lanciare il metodo getUsers() sui valori del servizio
private servizio; constructor( private service: UserService) { this.servizio = service; } ngOnInit() { this.users = this.servizio.getUsers(); }
Il risultato non cambia, il metodo getUsers() viene lanciato solo all’avvio del componente, ma la prima forma è preferibile perchè più conpatta.