Angular - komunikacja z serwerem za pomocą HttpClient

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

Uruchamianie lokalnego serwera

Dalsza praca będzie wymagała wykorzystania wiedzy związanej z tworzeniem prostego lokalnego serwera opisanej na stronie Programowanie → Angular - podstawy → Angular - tworzenie lokalnego serwera na potrzeby serwisów. W tym przypadku dane, z których będę korzystał będą miały następującą postać:

Listing 1
  1. {
  2. "book": [
  3. {
  4. "id": "1",
  5. "title": "Rio Anaconda",
  6. "author": "Wojciech Cejrowski"
  7. },
  8. {
  9. "id": "2",
  10. "title": "Pan Tadeusz",
  11. "author": "Adam Mickiewicz"
  12. }
  13. ]
  14. }

zapisane w pliku json_data.json. Uruchomienie serwera wymaga wpisania w konsoli:

json-server json_data.json

Pobieranie danych z serwera

Czas najwyższy użyć obiektu klasy HttpClient, dzięki któremu możliwe będzie łatwe pobieranie danych z serwera. W tym celu w module głównym app.module.ts należy zaimportować moduł HttpClientModule a następnie stworzyć nowy serwis:

ng g s FirstContact

Co spowoduje utworzenie nowego serwisu. Oto jak będzie wstępnie wyglądała klasa tego serwisu z możliwością pobierania danych z serwera:

Listing 2
  1. import { Injectable } from '@angular/core';
  2. import { Observable } from 'rxjs';
  3. import { HttpClient } from '@angular/common/http';
  4. @Injectable({
  5. providedIn: 'root'
  6. })
  7. export class FirstContactService {
  8. basePath = `http://localhost:3000`;
  9. constructor(private http: HttpClient) { }
  10. constructor(private http: HttpClient) { }
  11. getBooks(): Observable<Book[]> {
  12. return this.http.get<Book[]>(`${this.basePath}/book`);
  13. }
  14. }

Teraz już wystarczy wykorzystać tak stworzony serwis do pobrania danych z serwera:

Listing 3
  1. ngOnInit() {
  2. this.firstContact.getBooks().subscribe(
  3. (books) => {
  4. console.log('books', books);
  5. },
  6. (error: HttpErrorResponse) => {
  7. console.log('Błąd: ', error);
  8. }
  9. );
  10. }

oczywiście konieczne będzie też wstrzyknięcie serwisu w konstruktorze:

Listing 4
  1. constructor(
  2. private firstContact: FirstContactService
  3. ) {}

Wynik działania kodu powinien być następujący:

books 
(2) []
0: Object { id: "1", title: "Rio Anaconda", author: "Wojciech Cejrowski" }
1: Object { id: "2", title: "Pan Tadeusz", author: "Adam Mickiewicz" }
length: 2
<prototype>: Array[]

Możliwe jest oczywiście pobieranie pojedynczego elementu z listy książek, wystarczy utworzyć takie oto zapytanie:

Listing 5
  1. getBook(id: number): Observable<any> {
  2. return this.http.get(`http://localhost:3000/book/${id}`);
  3. }

Jak widać użyłem tutaj niezbyt przyjaznego typu any, warto go zastąpić interfejsem opisującym zawartość zwracanych danych:

Listing 6
  1. import { Injectable } from '@angular/core';
  2. import { Observable } from 'rxjs';
  3. import { HttpClient } from '@angular/common/http';
  4. export interface Book{
  5. id: number;
  6. title: string;
  7. author: string;
  8. }
  9. @Injectable({
  10. providedIn: 'root'
  11. })
  12. export class FirstContactService {
  13. basePath = `http://localhost:3000`;
  14. constructor(private http: HttpClient) { }
  15. getBooks(): Observable<Book[]> {
  16. return this.http.get<Book[]>(`${this.basePath}/book`);
  17. }
  18. getBook(id: number): Observable<Book> {
  19. return this.http.get<Book>(`${this.basePath}/book/${id}`);
  20. }
  21. }

Dodawanie nowego rekordu do bazy danych

Dodanie nowej książki, czyli nowego elementu do bazy danych na serwerze według specyfikacji powinno polegać na wysłaniu obiektu książki bez id na POST-a, czyli tym razem metoda w serwisie będzie wyglądała tak:

Listing 7
  1. addBook(book: Book): Observable<any> {
  2. return this.http.post(`${this.basePath}/book`, book);
  3. }

Teraz jest okazja, aby wykorzystać operatory RxJs:

Listing 8
  1. const book: Book = { id: null, title: 'Gringo wśród Dzikich plemion', author: 'Wojciech Cejrowski' };
  2. this.firstContact.addBook(book)
  3. .pipe(
  4. tap(() => { // dzięki temu operatorowi w międzyczasie po przyjściu odpowiedzi zwrotnej na put-a
  5. this.firstContact.getBooks().subscribe( // pobierz nową listę książek
  6. (books) => {
  7. console.log('books', books);
  8. },
  9. (error: HttpErrorResponse) => {
  10. console.log('Błąd: ', error);
  11. }
  12. );
  13. })
  14. ).subscribe(
  15. (value) => {
  16. console.log('Zakończono i zwrócono: ', value); // zwrócona wartość przez zapytanie na put-a
  17. },
  18. (error: HttpErrorResponse) => { // a tutaj błąd jakby coś poszło nie tak
  19. console.log('Zakończono z błędem: ', error);
  20. }
  21. );

Wynik działania w konsoli powinien wyglądać następująco:

Zakończono i zwrócono:  
Object { id: "FTQDCYl", title: "Gringo wśród Dzikich plemion", author: "Wojciech Cejrowski" }

books 
(3) []
0: Object { id: "1", title: "Rio Anaconda", author: "Wojciech Cejrowski" }
1: Object { id: "2", title: "Pan Tadeusz", author: "Adam Mickiewicz" }
2: Object { id: "vtI1lvc", title: "Gringo wśród Dzikich plemion", author: "Wojciech Cejrowski" }
length: 3
: []

Najpierw przyszedł nagłówek potwierdzający, że dane zostały wysłane prawidłowo i zapisane na serwerze, następnie poszło zapytanie o książki, które zwróciło nową listę książek. Gdyby pierwsze żądanie się nie powiodło, to drugie również by się nie powiedzie drugie nie zostanie wysłane.

Zapisywanie zmian w bazie

Kolejne zapytanie to PUT, oznacza ono, że dane są wysyłane w celu zapisania zmian w rekordzie w bazie danych. Oto jak wygląda metoda w serwisie:

Listing 9
  1. saveBook(book: Book): Observable<any> {
  2. return this.http.put(`${this.basePath}/book/${book.id}`, book);
  3. }

Jak widać książka zostanie zapisana a jej id koniecznie trzeba podać w ścieżce. W przypadku POST-a nie było to konieczne z tego prostego względu, że to baza danych nadaje id wpisowi, gdy ten jest do bazy danych dodawany.

Usuwanie elementu z bazy danych

Do serwera można wysłać zapytanie DELETE, którego celem jest usunięcie wpisu z bazy danych. Metoda w serwisie będzie wyglądała następująco:

Listing 10
  1. deleteBook(book: Book): Observable<any> {
  2. return this.http.delete(`${this.basePath}/book/${book.id}`);
  3. }

Wysyłanie dodatkowych parametrów w ścieżce zapytania

HttpClient umożliwia wysyłanie dodatkowych informacji w nagłówku. Ich wadą jest to, że są one jawne i mają ograniczone możliwości (nie można w ten sposób przesyłać całych obiektów). Czasami jednak mogą się przydać np. do pobierania tylko ograniczonej liczby rekordów, określenia warunków sortowanie itd. itp. Oto jak w prosty sposób można utworzyć taką metodą w serwisie:

Listing 11
  1. getBooks(limit: number = 0): Observable<Book> {
  2. let params: HttpParams = new HttpParams();
  3. if (limit) {
  4. params = params.set('limit', limit.toString());
  5. }
  6. return this.http.get<Book>(`${this.basePath}/book`, { params });
  7. }

Jak widać, gdy parametr limit będzie różny od 0 to dodane do adresu zostanie query z limitem elementów do pobrania. Metoda set zwraca instancję zmodyfikowanego obiektu HttpParams. Przykładowo ścieżka dostępu może wyglądać tak: localhost:3000/book?limit=50.

Gdyby parametrów było więcej ścieżka mogłaby wyglądać tak: localhost:3000/book?limit=50&offset=150.

Komentarze