Angular - komunikacja z serwerem za pomocą HttpClient

Autor podstrony: Krzysztof Zajączkowski

Stronę tą wyświetlono już: 2943 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ć:

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

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:

import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { HttpClient } from '@angular/common/http'; @Injectable({ providedIn: 'root' }) export class FirstContactService { basePath = `http://localhost:3000`; constructor(private http: HttpClient) { } constructor(private http: HttpClient) { } getBooks(): Observable<Book[]> { return this.http.get<Book[]>(`${this.basePath}/book`); } }

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

ngOnInit() { this.firstContact.getBooks().subscribe( (books) => { console.log('books', books); }, (error: HttpErrorResponse) => { console.log('Błąd: ', error); } ); }

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

constructor( private firstContact: FirstContactService ) {}

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:

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

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

import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { HttpClient } from '@angular/common/http'; export interface Book{ id: number; title: string; author: string; } @Injectable({ providedIn: 'root' }) export class FirstContactService { basePath = `http://localhost:3000`; constructor(private http: HttpClient) { } getBooks(): Observable<Book[]> { return this.http.get<Book[]>(`${this.basePath}/book`); } getBook(id: number): Observable<Book> { return this.http.get<Book>(`${this.basePath}/book/${id}`); } }

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:

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

Teraz jest okazja, aby wykorzystać operatory RxJs:

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

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:

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

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:

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

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:

getBooks(limit: number = 0): Observable<Book> { let params: HttpParams = new HttpParams(); if (limit) { params = params.set('limit', limit.toString()); } return this.http.get<Book>(`${this.basePath}/book`, { params }); }

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.

Propozycje książek