W poprzednich artykułach zajmowaliśmy się głównie tematyką komponentów, których zadaniem jest pośredniczenie pomiędzy widokiem a logiką aplikacji. W Angular 2 klasy komponentów powinny być jak najbardziej przystępne – każde nietrywialne zadanie, takie jak chociażby zapytania XHR, powinno być oddelegowane do serwisów.
Czym są serwisy?
Serwisy reprezentowane są przez klasy. Ich zakres zastosowań jest ogromny – mogą służyć jako typowi dostawcy, np. stałych danych (z ang. provider), jako pośrednicy w interakcji z zewnętrznym API czy nasłuchiwać socket i emitować zdarzenia nasłuchiwane przez inne serwisy.
Serwisy – tak samo jak komponenty – mogą opierać się na zależnościach – tzn. odpowiednie instancje obiektów zostaną im wstrzyknięte przez konstruktor (np. inne serwisy).
Pierwszy serwis
Aby zaprezentować w jaki sposób tworzyć serwisy, wróćmy do naszej Todo App. Dotychczas – w momencie uruchomienia aplikacji – nasza lista zadań do wykonania pozostawała pusta. Nasz cel jest prosty – dorzucimy pewien zbiór już utworzonych zadań, które powinny się pojawić. W oknie wiersza poleceń (korzystamy z CLI) wpisujemy następujący wiersz, aby utworzyć klasę serwisu:
ng g service todos
Powyższe polecenie powinno utworzyć nam plik o nazwie todos.service.ts
i następującej zawartości:
import { Injectable } from '@angular/core';
@Injectable()
export class TodosService {
constructor() { }
}
Pierwszym, co przykuwa nasz wzrok jest zapewne adnotacja @Injectable()
. Prostymi słowy oznacza ona, że nasz serwis jest zdolny do wstrzykiwania zależności przez konstruktor. Bez niej próba zakończy się niepowodzeniem.
Dorzućmy do naszej klasy metodę, która zwróci początkowe elementy listy:
getTodos(): Todo[] {
return [
{ name: "Todo 1", status: TodoStatus.BUG },
{ name: "Todo 2", status: TodoStatus.TODO },
{ name: "Todo 3", status: TodoStatus.IN_REVIEW },
]
}
To wszystko. Przenosimy się do głównego komponentu naszej aplikacji – AppComponent
– który znajduje się w pliku app.component.ts
i zastępujemy dotychczasowy konstruktor następującym kodem:
constructor(private todosService: TodosService) {
this.todos = todosService.getTodos();
}
Uwaga! Konstruktor nie jest dobrym miejscem na tego typu działania. Naturalnie powinniśmy wykorzystać zdarzenie
OnInit
komponentu. Wykorzystałem konstruktor, ponieważ akurat w naszym przypadku nic to nie zmienia.
Zapisujemy, kompilujemy i po przeniesieniu się do okna przeglądarki, w konsoli ukaże się nam następujący komunikat błędu:
No provider for TodosService!
Aby Angular 2 mógł wstrzyknąć nam zależność, musimy najpierw poinformować go o istnieniu tego, czym chcemy się posłużyć. Dopisujemy więc nasz serwis do obiektu będącego argumentem adnotacji @Component
– do tablicy providers
:
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
providers: [ TodosService ]
})
Po kompilacji w oknie przeglądarki powinniśmy zobaczyć działającą aplikację z zaciągniętymi danymi początkowymi:
Zasięg providers
W naszym przypadku Injector
będzie tworzył instancję serwisu TodosService
specjalnie na wyłączność komponentu AppComponent
. Takie podejście w większości przypadków nie ma sensu. Co w sytuacji, kiedy chcielibyśmy współdzielić obiekt pomiędzy kilkoma komponentami, np. w obrębie całego modułu? Nic prostszego.
Usuwamy poprzednio utworzoną tablicę providers
w argumencie adnotacji @Component
i przenosimy się do pliku modułu – app.module.ts
(o tym czym są i jak działają moduły planuję napisać w oddzielnym artykule).
Tym razem do adnotacji @NgModule
wrzucamy taką samą tablicę providers
, jaką poprzednio wykorzystaliśmy w przypadku @Component
. Tak powinien prezentować się nasz uzupełniony nowym serwisem moduł:
@NgModule({
declarations: [
AppComponent,
AddTodoComponent,
TodoItemComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule
],
providers: [
TodosService
],
bootstrap: [ AppComponent ]
})
export class AppModule { }
I to wszystko. Teraz nasz TodosService
będzie dostępny z poziomu wszystkich komponentów (jak i serwisów!) należących do modułu AppModule
.
Podsumowanie
Celem artykułu było zaprezentowanie podstawowej wiedzy dotyczącej serwisów w Angular 2 – czym są, jak działają i w jaki sposób je wykorzystać. W następnym wpisie zajmiemy się tematem nieco bardziej dogłębnie, m.in. wykorzystamy możliwości modułu HttpModule
.
Pełny kod źródłowy dostępny na GitHub.