Riassumendo quanto fatto fino ad ora, abbiamo aggiunto al nostro backend un metodo per avere la singola categoria, i dati in risposta sono integrati con un URL che richiama una nuova API che fornisce tutti i prodotti per quella categoria.
Ora creiamo il nuovo component tramite la CLI:
Ng generate component prodotti/categorie-prodotti
Aggiungiamo una rotta da hoc al modulo del routing
e prepariamo un template essenziale, non ci occuperemo tanto del binding dei dati quanto della logica delle chiamate nel component; pertanto il template HTML sará il seguente:
<div class="container">
<h1>Angular 2</h1>
<div class="col-md-3">
<pre>
{{categorie | json}}
</pre>
</div>
<div class="col-md-3">
<pre>
{{prodotti | json}}
</pre>
</div>
<div class="col-md-3">
<pre>
{{prodotti2 | json}}
</pre>
</div>
<div class="col-md-3">
<pre>
{{dati | json}}
</pre>
</div>
</div>
Al component dedicheremo maggiore attenzione a partire dagli import:
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
Il decoratore del component sará invece:
@Component({
templateUrl: './categorie-prodotti.component.html',
styleUrls: ['./categorie-prodotti.component.css']
})
Mentre all'interno della classe andiamo a dichiarare alcune proprietá, mentre l'URL da chiamare verrà impostata staticamente per velocizzare il lavoro:
private url1 = 'http://localhost/phprest/api/v1/categorie/1';
categorie: {};
prodotti: {};
dati: {};
Passiamo al costruttore HttpClient
in modo da eseguire le richieste direttamente dal component senza far leva su servizi esterni:
constructor(private http: HttpClient) { }
Mentre per l'evento ngOnInit
procediamo con la prima chiamata classica che non prevede grandi novitá:
this.http.get(this.url1).subscribe(json => { this.categorie = json; } );
La questione si fa interessante quando vogliamo caricare a cascata rispetto alla chiamata della categoria quella relativa ai prodotti per categoria. Una soluzione scontata é quella di annidare la seconda chiamata nella callback:
this.http.get(this.url1).subscribe(json => {
this.http.get(json['prodotti']).subscribe(data => {
json['prodotti'] = data;
this.prodotti = json;
});
});
La seconda chiamata inserirá l'array in risposta nella proprietá prodotti
della prima chiamata.
Questo sistema rischia peró di diventare oneroso quando le chiamate da annidare si moltiplicano. Una soluzione viene fornita dall'operatore flatMap che ci permette di combinare richieste sequenziali. Per variare il codice e descrivere una soluzione alternativa creeremo una funzione da richiamare all'evento di inizializzazione del component.
getCategoryWithProducts(): Observable {
return this.http2.get(this.url1)
.map((res: any) => res.json())
.flatMap((categoria: any) => {
return this.http2.get(categoria.prodotti)
.map((res: any) => {
let prodotti = res.json();
categoria.prodotti = prodotti;
return categoria;
});
});
}
Ecco le nostre richieste in serie: nello specifico l'operatore flatMap concatena i due oggetti observable
in un unico oggetto observable
che viene poi restituito. Non ci resta che richiamare la funzione all'evento onInit
e attendere la risposta:
this.getCategoryWithProducts().subscribe(json => { this.prodotti2 = json; } );
Se invece vogliamo effettuare le nostre richieste in parallelo, una soluzione é offerta dall'operatore forkJoin, anche in questo caso andremo a fondere le risposte al termine della ricezione dei dati dell'ultima chiamata:
const categorie = this.http.get(this.url1);
const prodotti = this.http.get('http://localhost/phprest/api/v1/categorie-prodotti/1');
forkJoin([categorie, prodotti]).subscribe(results => {
results[0]['prodotti']=results[1];
this.dati = results[0];
});