Autor podstrony: Krzysztof Zajączkowski

Stronę tą wyświetlono już: 5358 razy

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:

{ "book": [ { "id": "1", "title": "Rio Anaconda", "author": "Wojciech Cejrowski" }, { "id": "2", "title": "Pan Tadeusz", "author": "Adam Mickiewicz" } ] }

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:

ng g c bookList

Do wczytywania danych z serwera wykorzystam serwis opisywany już wcześniej na stronie Programowanie → Angular - podstawy → Angular - komunikacja z serwerem za pomocą HttpClient. Natomiast w HTML-u komponentu wykorzystam materialową tabelkę. Żeby jednak było to możliwe konieczne jest zainstalowanie biblioteki material w następujący sposób:

npm install @angular/material

a następnie dodać do projektu:

ng add @angular/material --save

Teraz w module komponentu np. app.module.ts konieczne będzie zaimportowanie modułu MatTableModule.

Po wykonaniu wszystkich tych niezmiernie nużących czynności można zacząć tworzyć swoją pierwszą tabelkę materialową w HTML-u komponentu:

<div *ngIf="getBooks | async"> <mat-table [dataSource]="dataSource" class="material-table"> <ng-container matColumnDef="position"> <mat-header-cell *matHeaderCellDef> Nr. </mat-header-cell> <mat-cell *matCellDef="let element; let index = index"> {{ index + 1 }} </mat-cell> </ng-container> <ng-container matColumnDef="title"> <mat-header-cell *matHeaderCellDef> Tytuł </mat-header-cell> <mat-cell mat-cell *matCellDef="let element"> {{element.title}} </mat-cell> </ng-container> <ng-container matColumnDef="author"> <mat-header-cell *matHeaderCellDef> Autor </mat-header-cell> <mat-cell mat-cell *matCellDef="let element"> {{element.author}} </mat-cell> </ng-container> <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row> <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row> </mat-table> </div>

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ę.

Layout wykonany przez autora strony, wszelkie prawa zastrzeżone. Jakiekolwiek użycie części lub całości grafik znajdujących się na tej stronie bez pisemnej zgody jej autora surowo zabronione.