27 mar 2017

Połączenie backendu z frontendem, czyli odbieranie danych z API w Angular

W dzisiejszym poście zamierzam pokazać jak odebrać dane z API w Angular i wyświetlić je w aplikacji - czyli ciąg dalszy spinania aplikacji w całość. Na razie wyświetlę tylko suche dane, bez "ubierania" ich w wykres - na ten temat pojawi się osobny post.
Odbieranie JSON w Angular
Do celów pobierania danych z API utworzyłem dedykowany serwis który będzie odpowiedzialny za to zadanie. Jego kod wygląda następująco:
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';

@Injectable()
export class ApiService {

  private baseUrl: string = 'http://localhost:5000';

  constructor(private http : Http) { }

  getTestData() : Observable {
    return this.http.get(`${this.baseUrl}/dbtest`)
    .map(this.extractData)
    .catch(this.handleError);
  }

  private extractData(res: Response) {
    let body = res.json();
    return body.data || { };
  }

  private handleError (error: Response | any) {
    // In a real world app, we might use a remote logging infrastructure
    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();
    }
    console.error(errMsg);
    return Observable.throw(errMsg);
  }
}
Serwis jest oznaczony dekoratorem @Injectable(), dzięki czemu będzie dodawany do komponentów przez wstrzykiwanie zależności. Tak samo jest w tej klasie wczytany serwis Http, dzięki któremu wyślemy żądanie do API - w konstruktorze podajemy tylko, że chcemy go używać i nie martwimy się jego utworzeniem. Następnie mamy właściwą metodę getTestData() do pobrania danych, gdzie wykorzystujemy serwis Http i obsługujemy powodzenie żądania oraz ewentualny błąd. Metoda ta zwraca obiekt Observable, czyli coś co będziemy mogli obserwować i co powiadomi nas kiedy zmieni swoją zawartość - czyli kiedy żądanie ukończy swoje działanie. Zanim jednak będę mógł takie dane odebrać i wyświetlić muszę zwrócić uwagę na jeszcze jedną rzecz.

Co to jest CORS i dlaczego ma znaczenie?
Skrót CORS oznacza Cross-origin resource sharing. Jest to mechanizm umożliwiający pobranie zasobów przy użyciu XMLHttpRequest (takie zapytania wysyła Angular i w ogólności JavaScript) z zewnętrznego serwera. W praktyce sprowadza się do tego, że jeśli chcemy uzyskać zasoby z serwera z innej domeny, to ten serwer musi w takiej odpowiedzi zawierać nagłówek Access-Control-Allow-Origin z naszą domeną.
Dlaczego o tym piszę i dlaczego to ważne? Temat ten dotyczy mojego projektu, bo API będzie na innym serwerze niż interfejs użytkownika, muszę więc zapewnić odpowiednią obsługę żądań cross domain. Na szczęście Flask ma gotową bibliotekę do CORS i uruchomienie sprowadza się do dodania w backendzie importu modułu
from flask_cors import CORS, cross_origin
i poniżej oznaczeniu że aplikacja ma go używać
CORS(app)
Użycie ApiService i wyświetlenie odebranych danych
Dzięki temu, że mam już gotowy serwis do wysłania żądania do serwera i wstępnej obróbki danych, to samo jego użycie jest bardzo proste. Definiuję zmienną gdzie zostaną zapisane dane, wpisuję w konstruktorze że ma zostać wstrzyknięty serwis i wywołuję metodę tego serwisu przy inicjalizacji komponentu. Metoda first() informuje, że zwrócony z getTestData() obiekt Observable ma poinformować o jednej zmianie swojego stanu i zakończyć działanie. Do metody subscribe() natomiast przekazuję funkcję, która zostanie wykonana kiedy Observable zmieni swój stan - czyli kiedy serwer odpowie na zapytanie i odbierzemy odpowiedź. Kod prezentuje się następująco:
export class AppComponent implements OnInit {

  testData : any[];

  constructor(private apiService: ApiService) { }

  ngOnInit(): void {
    this.apiService.getTestData().first().subscribe(response => this.testData = response);
  }
}
Pozostaje już tylko wyświetlić te dane, więc w pliku html wpisuję
Dane z api:<br />{{testData}}
Dane odebrane z API wyświetlają się, zatem wszystkie warstwy aplikacji komunikują się i działają poprawnie. Mam więc w końcu szkielet, na którym będę mógł dalej rozwijać swoją aplikację. Mam nadzieję, że teraz jej rozwój ruszy z kopyta :) Ciągle jednak więcej czasu spędzam nad pisaniem postów niż nad kodem, dlatego muszę nieco zmienić koncepcję bloga - prawdopodobnie będzie trochę mniej technicznie. Po więcej szczegółów zapraszam w czwartek!

0 komentarze:

Prześlij komentarz