Angular udostępnia pipe-a o nazwie async. Umożliwia on asynchroniczne wykonanie zapytania do serwera, dzięki czemu dany element strony będzie w stanie wyświetlić się prawidłowo. Prosty przykład wykorzystania tego pipa będzie wymagał użycia omawianego wcześniej na stronie Programowanie → Angular - podstawy → Angular - tworzenie lokalnego serwera na potrzeby serwisów serwera lokalnego. Jego uruchomienie będzie wymagało obecności np. takiego przykładowego zbioru danych:
Zapisanego w pliku json_data.json. Teraz start serwera za pomocą polecenia:
json-server json_data.json
Dla sprawdzenia w przeglądarce można wpisać: localhost:3000 by sprawdzić, czy wszystko działa prawidłowo. Jeżeli tak to czas stworzyć sobie komponent, który w jakiś w miarę przyzwoity sposób wyświetli te dane. Tak więc tworzę sobie komponent o nazwie booksList:
W pierwszej linijce wczytywane są asynchronicznie dane. Zmienna dataSource to specjalna zmienna przechowująca między innymi dane do wyświetlania w tabelce. Natomiast displayedColumns to zmienna, która zawiera nazwy wyświetlanych kolumn w postaci tablicy string-ów oznaczających nazwy plików. Znacznik ng-container opisuje zawartość kolumny identyfikowanej za pomocą atrybutu matColumnDef będącego nazwą kolumny. Znacznik th zawierający dyrektywę mat-header-cell określa nagłówek kolumny, natomiast dane są wyświetlane w znaczniku td. Odwołanie się do danych jest możliwe dzięki dyrektywie *matRowDef, w której można utworzyć odwołanie do rekordu danych:
*matCellDef="let element"
Możliwe jest również uzyskanie indeksu danego rekordu:
*matCellDef="let element; let index = index"
Dane dotyczące kolumn nagłówka i rekordów danych zostały zapisane na końcu tabelki. Odpowiednie dyrektywy mat-header-row i mat-row wiążą dany znacznik th z nagłówkiem lub danymi.
Przejdźmy jednak do kodu samego komponentu, albowiem treścią tego tematu nie jest tworzenie tabelek, a użycie samego asynchronicznego ładowania danych. Oto kod komponentu:
import { Component, OnInit } from '@angular/core';
import { Book, FirstContactService } from '../submodule/first-contact.service';
import { MatTableDataSource } from '@angular/material';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Component({
selector: 'app-books-list',
templateUrl: './books-list.component.html',
styleUrls: ['./books-list.component.css']
})
export class BooksListComponent implements OnInit {
// zmienna zawierająca między innymi dane tabelki
dataSource: MatTableDataSource<Book> = new MatTableDataSource<Book>();
// zmienna zawierająca nazwy wyświetlanych kolumn (określa również położenie danej kolumny w tabeli)
displayedColumns: string[] = ['position', 'title', 'author'];
constructor(
public firstContactService: FirstContactService
) { }
// ten getter jest wykorzystywany do ładowania asynchronicznym pipe-m danych do tabelki
get getBooks(): Observable<Book[]> {
if (this.dataSource.data.length === 0) {
return this.firstContactService.getBooks()
.pipe(
tap((books: Book[]) => {
this.dataSource.data = books;
console.log('First');
}),
map((books: Book[]) => this.dataSource.data)
);
}
return of(this.dataSource.data).pipe(tap(() => console.log('Second')));
}
ngOnInit() {
}
}
Co trzeba zrobić, aby dane przeładować asynchronicznie? W tym przypadku trzeba wyczyścić pole data pola dataSource komponentu. Nie jest to najlepsze rozwiązanie, ponieważ jeżeli w bazie danych nie będzie wpisu to żądanie cały czas będzie ponawiane. Jest jednak to dobre rozwiązanie, gdy wiadomym jest, że dane takie muszą istnieć w bazie danych. Dla przykładu, użytkownik się zalogował pobierając dane dostępowe do różnych elementów systemu jakim jest tworzony przez ciebie program. W takim przypadku konieczny będzie dostęp do tych danych aby poprawnie wyświetlać widok. Takie więc asynchronicznie ładowane pozwoli na odzyskiwanie danych, gdy np. użytkownik odświeży stronę.