Javascript

Kurs Angular 2 – Warstwa serwisów, podstawy

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:

app

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.