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.