W poprzednim artykule omówiłem podstawowe informacje dotyczące serwisów – w jaki sposób je tworzyć, gdzie i kiedy stosować. Dzisiaj rozszerzymy te zagadnienia o stworzenie serwisu komunikującego się z zewnętrznym API, wykorzystując dostarczony przez twórców Angular 2 HttpModule
.
HttpModule i JsonpModule
Moduły HttpModule
i JsonpModule
udostępniają nam gotowe serwisy – odpowiednio Http
i Jsonp
, które możemy swobodnie wstrzyknąć przez konstruktor naszego serwisu, dzięki czemu tworzenie aplikacji komunikujących się z zewnętrznym serwerem w Angular 2 jest bardzo wygodne i wymaga od nas minimalnego nakładu pracy.
Aby zaprezentować działanie serwisu Http
, wykorzystamy kod z poprzedniego artykułu, w którym zastąpimy stały zbiór danych zwracany przez serwis na rezultat zapytania GET
z zewnętrznego API. Dodatkowo dorzucimy możliwość dodania nowego zadania do listy przy pomocy metody POST
.
Uwaga! Zanim będziemy mogli korzystać z serwisów udostępnianych przez moduły, musimy je wcześniej dopisać do listy
imports
modułu – w naszym przypadku jest toAppModule
iHttpModule
już się na niej znajduje.
Pobieramy dane z API
Rozpoczniemy od udoskonalenia naszego TodosService
znajdującego się w pliku todos.service.ts
. W pierwszej kolejności musimy wstrzyknąć zależność przez konstruktor:
constructor(private http: Http) {}
Następnie zastąpimy obecną metodę getTodos()
zwracającą stałe dane na metodę wykorzystującą klasę Http
(założyłem, że nasz serwer zewnętrzny znajduje się pod adresem http://localhost:3030/
):
getTodos(): Observable<Todo[]> {
return this.http.get("http://localhost:3030/users/")
.map(this.extractData)
.catch(this.handleError);
}
Szybko możemy zauważyć, że metoda get()
(jak i również pozostałe metody w przypadku obiektu klasy Http
) zwracają obiekt typu Observable. Dodatkowo wykorzystaliśmy dwie metody pomocnicze wprost z oficjalnej dokumentacji extractData()
i handleError()
, których ciała umieściłem poniżej:
private extractData(res: Response) {
const body = res.json();
return body.data || { };
}
private handleError (error: Response | any) {
let errMsg: string;
if (error instanceof Response) {
const body = error.json() || '';
const err = body.error || JSON.stringify(body);
errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
}
else {
errMsg = error.message ? error.message : error.toString();
}
return Observable.throw(errMsg);
}
Ich przeznaczenie wydaje się być zrozumiałe.
W ramach naszego TodosService
to już wszystkie modyfikacje, jakich musieliśmy dokonać. Przenosimy się do naszego AppComponent
(znajdującego się w pliku app.component.ts
) aby wykorzystać zwracany z poziomu serwisu obiekt typu Observable
. Musimy przekształcić dotychczasowy kod konstruktora oraz zaimplementować dwa interfejsy OnInit
oraz OnDestroy
:
export class AppComponent implements OnInit, OnDestroy {
public todos: Todo[];
private todos$: Subscription;
constructor(private todosService: TodosService) {}
ngOnInit() {
this.todos$ = this.todosService.getTodos()
.subscribe((todos) => this.todos = todos);
}
ngOnDestroy() {
if(this.todos$) this.todos$.unsubscribe();
}
...
}
Jak możemy zauważyć, subskrybujemy zwracany obiekt Observable
, dzięki czemu w przypadku uzyskania wartości, będziemy mogli uzupełnić tablicę przechowywaną w klasie komponentu. Dodatkowo przechowujemy obiekt subskrypcji w zmiennej todos$
, co pozwoli nam w momencie niszczenia instancji komponentu zwolnić subskrypcję (unikniemy ewentualnych wycieków pamięci).
Zapytania POST
Nasza aplikacja korzysta już z realnych danych zaciąganych z zewnętrznego serwisu. Teraz dorzucimy jej jeszcze jedną funkcjonalność – możliwość wysyłania nowych zadań przy pomocy metody POST
.
Wracamy do naszego TodosService
i dorzucamy metodę addTodo()
:
addTodo(todo: Todo) {
const headers = new Headers({ 'Content-Type': 'application/json' });
const options = new RequestOptions({ headers: headers });
return this.http.post("http://localhost:3030/todos/", { todo }, options)
.map(this.extractData)
.toPromise()
.catch(this.handleError);
}
Jak widzimy – budowanie zapytania typu POST
jest równie proste. Warto zwrócić uwagę, że w tym przypadku zdecydowałem się dorzucić wywołanie metody toPromise()
, dzięki czemu zwrócony zostanie nam obiekt typu Promise
a nie Observable
Ponownie przenosimy się do AppComponent
i zastępujemy poprzednią funkcję addTodo()
:
public addTodo(todo: Todo) {
this.todosService.addTodo(todo)
.then(() => this.todos.unshift(todo));
}
To wszystko!
Podsumowanie
W dzisiejszym artykule zaprezentowałem możliwości modułu dostarczonego przez zespół Angulara – HttpModule
, dzięki czemu nasz TodosService
stał się pełnoprawnym serwisem korzystającym z zewnętrznego API. W artykule nie prezentowałem działania Jsonp
, ponieważ praca z nim jest niezwykle podobna.
Pełny kod źródłowy dostępny na GitHub.