Czy zawsze musimy odświeżać dane po każdej akcji? A może dąłoby się to zrobić troszkę inaczej i lżej? Sami zobaczcie.
Jedna lista
Generalnie dzisiaj nie będzie nic odkrywczego. Jedynie taki powiedzmy good practice. Jeśli mamy np moduł, który zarządza zespołami, to warto trzymać go w jednym stałym miejscu dla całej instancji aplikacji. Takim miejscem jest właśnie dedykowany serwis. Serwisy mają to do siebie, że tworzone są tylko raz. A dzięki temu jeśli to w nich będziemy trzymać dane to te dane będą załadowane jednorazowo. Wyobraźmy sobie, że chodzimy pomiędzy widokami. Jeśli pobieranie listy z API będzie na poziomie komponentów to wtedy będziemy pobierać dane za każdym razem (zmiana routingu to nowa instancja komponentu). A to może obciążyć docelowo nasz serwer! Dlatego warto pobieranie zrobić po stronie serwisu i potem jedynie sprawdzać, czy dane są już pobrane.
TeamsService
Oto i krótki kod:
import { Injectable } from '@angular/core';
import { Http, Headers, Response, RequestOptionsArgs } from '@angular/http';
import { Observable } from "rxjs/Rx";
import { ApplicationSettings } from "../../../../config/app.settings";
import { AppAuthorization } from '../../../../core/app.authorization'
import { Team } from "./models/team";
import { CreateTeamVM } from "./models/models";
@Injectable()
export class TeamsService {
teams: Array;
teamsLoaded: boolean;
API_URL: string;
constructor(private http: Http,
private AppAuthorization: AppAuthorization) {
this.API_URL = ApplicationSettings.apiURL;
this.teamsLoaded = false;
}
loadTeams() {
let url = this.API_URL + "/Teams";
var headers = this.AppAuthorization.createAuthorizationHeader(null);
return this.http.get(url, {
headers: headers
})
.map(response => response.json())
.do(data => {
this.teamsLoaded = true;
this.teams = data;
})
.catch(error => {
return Observable.throw(error);
});
}
createTeam(team: CreateTeamVM) {
let url = this.API_URL + "/Teams";
var headers = this.AppAuthorization.createAuthorizationHeader(null);
return this.http.post(url, team, {
headers: headers
})
.map(response => response.json())
.do(data => {
let newTeam = new Team();
newTeam.name = team.name;
newTeam.id = data;
this.teams.push(newTeam);
})
.catch(error => {
return Observable.throw(error);
});
}
}
Możecie zauważyć, że pobieranie zespołów powoduje natychmiastowo także ich zapisanie do lokalnej zmiennej i ustawienie flagi teamsLoaded. Podobnie jest z dodawaniem nowego zespołu. Nie musimy po takiej operacji przeładowywać całej listy a jedynie dodajemy tam jeden element i lista nadal jest w aktualnym stanie. A jak wygląda to po stronie widoku?
My teams component
import { Component, OnInit } from '@angular/core';
import { TeamsService } from "../../logic/teams.service"
import { Team } from "../../logic/models/team";
@Component({
templateUrl: './my-teams.html'
})
export class MyTeamsComponent implements OnInit {
teams: Array;
constructor(private teamsService: TeamsService) {
}
ngOnInit() {
if (this.teamsService.teamsLoaded) {
this.teams = this.teamsService.teams;
return;
}
this.teamsService.loadTeams()
.subscribe((data) => {
this.teams = this.teamsService.teams;
}, (error) => {
console.error(error);
});
}
}
Krótko, prawda? Na metodzie ngOnInit, która wywołuje się po załadowaniu komponentu sprawdzamy najpierw, czy dane są już załadowane. Jeśli tak - pobieramy statyczną kolekcję. Jeśli nie - wywołujemy ładowanie danych i bierzemy już kolekcję gotową. Proste, prawda?
Podsumowanie
Taki zabieg nie jest trudny. Ale dzięki niemu operujemy na raz pobranych danych i nie musimy zabijać naszego serwera.
Co dalej?
Generalnie powoli piszę kolejne kroki aplikacji. Niestety nawet bardzo powoli. Ale mimo wszystko idzie do przodu. Dosć duży projekt - a czasu coraz mniej. Ładna pogoda zachęca bardziej do spędzania czasu na powietrzu niż przed komputerem. Ale staram się przynajmniej raz w tygodniu na trochę do tego usiąść i dopisać kolejny klocek do całej budowli.
A tymczasem do usłyszenia!