You are on page 1of 16

4a.

Primer Angular aplikacije


Faza 1 – Prikaz jednog heroja i two-way data binding
Prikazaćemo sa kratkim komentarima izradu Angular aplikacije datu kao primer u okviru
Angular dokumentacije na strani: https://angular.io/tutorial

Kroz primer će biti prikazano sledeće:


 Korišćenje ugrađenih Angular direktiva za prikazivanje i skrivanje elemenata i
prikazivanje liste sa podacima o herojima;
 Kreiranje Angular komponenti za prikaz detalјa o herojima i prikazivanje niza heroja;
 Korišćenje one-way data binding za čitanje podataka (read-only data);
 Dodavanje polјa preko koga se mogu menjati podaci u modelu pomoću two-way
data binding;
 Povezivanje metoda komponenti sa događajima korisnika, kao što su klik mišem ili
pritiskanje/otpuštanje tastera na tastaturi;
 Omogućavanje korisniku da odabere element iz glavne liste i izmeni podatke u
prikazu detalјa za izabrani element;
 Formatiranje podataka pomoću pipes;
 Kreiranje delјenog servisa za assemble;
 Korišćenje rutiranja za navigaciju između različitih prikaza i njihovih komponenti.

Potrebno je da aplikacija ima sledeći izgled:

Slika 1. Dashboard aplikacije.

Dashboard i Heroes tasteri predstavlјaju linkove ka dva različita prikaza, prikaz za Dashboard
i prikaz za listu heroja.

Ispod naslova Top Heroes dati su najbolјi heroji, a klikom na nekog od njih dobija se prikaz
detalјa o tom heroju, kao na Slici 2, gde se može promeniti ime heroja ili se klikom na taster
„Back“ vratiti na Dashboard.
Ako se odabere taster Heroes, prikazuje se lista heroja, kao na Slici 3, a klikom na neko
drugo ime prikazuju se detalјi za tog heroja ispod liste, analogno kao na Slici 4, na kojoj je
prikazan dijagram svih opcija za navigaciju.

Slika 2. Prikaz detalјa o heroju.

Slika 4. Slika 3. Lista heroja.

Kreiramo inicijalnu aplikaciju pod nazivom „tour-of-heroes“ komandom:


ng new tour-of-heroes
čime Angular CLI kreira novi projekat sa podrazumevanom aplikacijom.

Iz direktorijuma aplikacije pozovemo Angular server komandom:


ng serve --open
čime se aplikacija prikazuje u pretraživaču, a Angular server nadgleda sve izmene u fajlovima
i vrši ponovno kreiranje aplikacije u zavisnosti od tih izmena.

U prozoru pretraživača prikazuje se tzv. lјuska aplikacije (application shell), tj. glavni prozor
aplikacije, koju kontroliše Angular komponenta pod nazivom AppComponent, koja je root
komponenta.

Ova aplikacija je u ovom stadijumu praktično ista kao aplikacija data na prethodnim
vežbama.
Prvo menjamo naslov aplikacije, tako što u app.component.ts fajlu dodamo unutar klase
atribut:
title = 'Tour of Heros';
dok u app.component.html fajlu unutar <h1> elementa upisujemo Angular markap:
<h1>{{title}}</h1>

Vitičaste zagrade predstavlјaju interpolation binding sintaksu Angular-a, i u ovom slučaju


predstavlјa prikaz vrednosti „title“ atributa unutar HTML head elementa. Angular server
detektuje promenu, vrši ponovno „bildovanje“ aplikacije i osvežava prikaz u pretraživaču.

U cilјu konzistentnog izgleda prikazanog sadržaja, potrebno je dodati CSS stilove za


odgovarajuće elemente u prikazu. Angular CLI kreira prazan styles.css, unutar koga se
postavlјaju stilovi koji se koriste u celoj aplikaciji. Na primer, možemo unutar tog CSS fajla
dodati sledeće stilove:
h1 {
color: #369;
font-family: Arial, Helvetica, sans-serif;
font-size: 250%;
}
h2, h3 {
color: #444;
font-family: Arial, Helvetica, sans-serif;
font-weight: lighter;
}
body {
margin: 2em;
}
body, input[text], button {
color: #888;
font-family: Cambria, Georgia;
}
/* everywhere else */
* {
font-family: Arial, Helvetica, sans-serif;
}

Sada ćemo dodati još jednu komponentu, koja će u glavnom prozoru aplikacije prikazivati
detalјe o heroju.

Novu komponentu „heroes“ kreiramo pomoću Angular CLI komande:


ng generate component heroes
nakon čega se kreira novi direktorijum „src/app/heroes/“ i tri fajla komponente
HeroesComponent. Nova klasa za ovu komponentu sadrži kôd:

import { Component, OnInit } from '@angular/core';


@Component({
selector: 'app-heroes',
templateUrl: './heroes.component.html',
styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {

constructor() { }

ngOnInit() {
}

}
@Component predstavlјa dekorator klase, unutar koga su navedeni podaci o CSS fajlu i
HTML šablonu za prikaz komponente, kao i tzv. CSS selektor za prikaz komponente unutar
HTML šablona.
Postavlјa se pitanje unutar kog HTML dokumenta ćemo prikazati heroes komponente?
Jednostavno, pošto je u pitanju komponenta koja je sadržana u glavnom modulu aplikacije,
sadržaj heroes komponente ćemo prikazati unutar HTML šablona glavne komponente
(app.component.html). Ispod naslova dodamo element:
<app-heroes></app-heroes>
Unutar klase HeroesComponent dodamo atribut:
hero = 'Windstorm';
i potom taj atribut prikažemo unutar heroes.component.html šablon fajla (obrisati sadržaj
koji je generisao Angular):
{{hero}}

Kako će u aplikaciji postojati više heroja, logično je da napravimo posebnu klasu za heroja,
pa ćemo unutar „src/app“ direktorijuma kreirati hero.ts dokument sa klasom:
export class Hero {
id: number;
name: string;
}
Koristimo klјučnu reč „export“ kako bi klasa bila dostupna drugim klasama (komponentama,
modulima).
Sada unutar HeroesComponenti klase uvezemo (import) Hero klasu:

import { Component, OnInit } from '@angular/core';


import { Hero } from '../hero';

@Component({
selector: 'app-heroes',
templateUrl: './heroes.component.html',
styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
hero: Hero = {
id: 1,
name: 'Windstorm'
};

constructor() { }

ngOnInit() {
}

Strana aplikacije se neće prikazati pravilno, jer hero više nije string, već objekat, pa je u
heroes.component.html potrebno da sadrži sledeći kod umesto prethodno datog:
<h2>{{ hero.name }} Details</h2>
<div><span>id: </span>{{hero.id}}</div>
<div><span>name: </span>{{hero.name}}</div>
nakon čega Angular ponovo rebilduje aplikaciju i pretraživač prikazuje osvežen sadržaj.
Ime heroja možemo prikazati velikim slovima, tako što ćemo koristiti pipe za konverziju u
velika slova, tj. uppercase, odnosno:
<h2>{{ hero.name | uppercase }} Details</h2>
Reč uppercase posle karaktera „|“ aktivira ugrađen UppercasePipe. Angular ima ugrađeno
nekoliko ovakvih mehanizama za formatiranje stringova, oznaka valuta, datuma i slično, a
moguće je definisati i dodatne mehanizme. Više o pipes može se pročitati ovde.

Prema specifikaciji, potrebno je da korisnik može izmeniti ime heroja, tj. potrebno nam je
<input> polјe. Ovo polјe mora sadržati ime heroja i takođe omogućiti da se u njega unesu
izmene, nakon čega se izmena imena heroja u instanci klase. To znači da podaci moraju teći
od komponent klase ka ekranu (tj. prikazu) i nazad sa prikaza do klase. Da bi se ovaj proces
automatizovao, Angular omogućava two-way data binding, u konkretnom slučaju između
<input> elementa i hero.name atributa, tako da će HeroesComponent šablon umesto ranije
datog sadržati sledeći kod:
<h2>{{ hero.name | uppercase }} Details</h2>
<div><span>id: </span>{{hero.id}}</div>
<div>
<label>name:
<input [(ngModel)]="hero.name" placeholder="name">
</label>
</div>

[(ngModel)] je Angular sintaksa za two-way data binding, tako da u ovom slučaju povezuje
hero.name atribut i HTML textbox, tako da podaci mogu da teku u oba smera.

Ovo nije dovolјno da bi aplikacija nastavila normalno da radi, jer se na konzoli (Inspector
panel) može videti greška:
Template parse errors:
Can't bind to 'ngModel' since it isn't a known property of 'input'.
Naime, ngModel nije dostupan podrazumevano, jer pripada FormsModule, koji se ne učitava
podrazumevano.
Zbog toga je u glavnom modulu (app.module.ts) potrebno importovati FormsModule na
sledeći način:
import { FormsModule } from '@angular/forms'; // <-- NgModel lives here
a takođe, ne treba zaboraviti FormsModule u „imports“ polјu @NgModule dekoratera:
imports: [
BrowserModule,
FormsModule
]
Sada aplikacija radi kako je predviđeno, i možemo menjati ime prikazanog heroja.

Na početku je rečeno da svaka komponenta mora biti deklarisana samo u jednom modulu,
tj. pripada jednom modulu. Mi u prethodnom postupku nigde nismo deklarisali
HeroesComponent, a aplikacija ipak radi. Kako?
Jednostavno, kada smo koristili Angular CLI za kreiranje nove komponente, Angular je
automatski dodao deklaraciju nove komponente unutar glavnog modula app.module.ts, gde
se može videti da je Angular dodao:
import { HeroesComponent } from './heroes/heroes.component';
i izmenio:
declarations: [
AppComponent,
HeroesComponent
]

Funkcionalnost do sada urađenog koda ogleda se u izmeni imena heroja prikazanog u


jednom elementu na osnovu izmena izvršenih unutar <input> elementa, kao što je prikazano
na Slici 5.

Slika 5. Angular two-way data binding na delu.


Do sada urađen kod (samo src direktorijum) postavlјen je na Moodle strani predmeta pod
nazivom Tour-of-heros (v1).

Faza 2 – prikaz liste heroja, *ngFor i *ngIf direktive


Prethodno urađen kod aplikacije prikazivao je jednog heroja. U praksi je češće slučaj da
postoji više instanci istog podatka sa različitim atributima, u ovom slučaju bi to značilo da
imamo više heroja.
Prvo ćemo kreirati listu heroja u fajlu „mock-heroes.ts“ u direktorijumu „src/app/“:
import { Hero } from './hero';

export const HEROES: Hero[] = [


{ id: 11, name: 'Mr. Nice' },
{ id: 12, name: 'Narco' },
{ id: 13, name: 'Bombasto' },
{ id: 14, name: 'Celeritas' },
{ id: 15, name: 'Magneta' },
{ id: 16, name: 'RubberMan' },
{ id: 17, name: 'Dynama' },
{ id: 18, name: 'Dr IQ' },
{ id: 19, name: 'Magma' },
{ id: 20, name: 'Tornado' }
];

Na ovaj način je definisan i eksportovan konstantan niz HEROES, pri čemu smo naravno prvo
uvezli klasu Hero, a HEROES definisali kao niz tipa Hero, pa je u pitanju niz koji sadrži
elemente tipa Hero.
Sada je potrebno importovati mock-heroes.ts unutar HeroesComponent klase:
import { HEROES } from '../mock-heroes';
U istoj komponenti umesto koda:
hero: Hero = {
id: 1,
name: 'Windstorm'
};
stavlјamo:
heroes = HEROES;
jer sada umesto jedne klase imamo niz objekata HEROES.
Sada u šalbonu komponente HeroComponent (heroes.component.html) dodamo iznad
postojećeg koda sledeći kod za prikaz jednog heroja u okviru HTML liste:
<h2>My Heroes</h2>
<ul class="heroes">
<li>
<span class="badge">{{hero.id}}</span> {{hero.name}}
</li>
</ul>
Da bismo mogli prikazivati dinamičke podatke u listi (npr. broj elemenata u listi se menja u
zavisnosti od neke promenlјive ili događaja), ne kreiramo celu listu ručno, već koristimo
Angular repeater direktivu *ngFor unutar početnog taga za element liste:
<li *ngFor="let hero of heroes">
gde je:
 <li> host element
 heroes je lista iz HeroesComponent klase
 hero je tekući objekat liste pri jednoj iteraciji kroz listu

Sada možemo dodati neke CSS stilove u heroes.component.css, npr.


/* HeroesComponent's private CSS styles */
.selected {
background-color: #CFD8DC !important;
color: white;
}
.heroes {
margin: 0 0 2em 0;
list-style-type: none;
padding: 0;
width: 15em;
}
.heroes li {
cursor: pointer;
position: relative;
left: 0;
background-color: #EEE;
margin: .5em;
padding: .3em 0;
height: 1.6em;
border-radius: 4px;
}
.heroes li.selected:hover {
background-color: #BBD8DC !important;
color: white;
}
.heroes li:hover {
color: #607D8B;
background-color: #DDD;
left: .1em;
}
.heroes .text {
position: relative;
top: -3px;
}
.heroes .badge {
display: inline-block;
font-size: small;
color: white;
padding: 0.8em 0.7em 0 0.7em;
background-color: #607D8B;
line-height: 1em;
position: relative;
left: -1px;
top: -4px;
height: 1.8em;
margin-right: .8em;
border-radius: 4px 0 0 4px;
}

Kada korisnik klikne na ime nekog od heroja, potrebno je za tog heroja prikazati detalјe, što
znači da moramo na događaj klika mišem pozvati metodu koja će prikazati detalјe za tog
heroja.
Prvo je potrebno dodati click event binding unutar elementa liste:
<li *ngFor="let hero of heroes" (click)="onSelect(hero)">
gde je (click)="onSelect(hero)" Angular event binding mehanizam za povezivanje
događaja u DOM-u metode koja se poziva pri tom događaju (tzv. template statement). Kod
Angular-a se povezivanje sa događajima vrši tako što se sa leve strane znaka jednakosti
navede naziv događaja u zagradama, a sa desne pod znacima navoda template statement.
Više o ovoj temi možete pogledati ovde.
Prvo kreiramo novu promenlјivu selectedHero kojoj će biti dodelјen objekat heroja na kog je
korisnik kliknuo, a potom definišemo metodu koja vrši tu dodelu nakon klika:
selectedHero: Hero;

constructor() { }

ngOnInit() {
}

onSelect(hero: Hero): void {


this.selectedHero = hero;
}
}

Sada je potrebno u HTML šablonu heroes.component.html umesto hero staviti selectedHero


u delu u kome se prikazuju detalјi.
Ostaje problem prikaza detalјa pri učitavanju aplikacije, jer korisnik još nije kliknuo na ime
nekog od heroja. Mogu se inicijalno prikazati detalјi prvog heroja i sl., ali se često koristi
uslovni prikaz, tako da se ništa ne prikazuje pre nego što korisnik klikne na neko ime. Nakon
tog klika, prikazuje se HTML blok koji sadrži detalјe o izabranom heroju.
To se postiže *ngIF direktivom, tako što se početni DIV tag izmeni na sledeći način:
<div *ngIf="selectedHero">
Na ovaj način, pre nego što korisnik klikne na neko ime iz liste, selectedHero nema
definisanu vrednosti, pa ngIf uklanja ceo blok iz DOM-a. Tek kad korisnik klikne na neko od
imena i selectedHero dobije neku definisanu vrednost, blok se dodaje u DOM, tako da biva
prikazan.

Za heroja na koga je korisnik kliknuo možemo izmeniti prikaz, npr. promeniti boju teksta i
pozadine, tako što unutar početnog <li> elementa za heroja dodamo:
[class.selected]="hero === selectedHero"
čime se elementu dodaje klasa ako je tekući objekat u iteraciji prikaza elemenata liste
jednak odabranom heroju po vrednosti i tipu. Na ovaj način se može uslovno dodeliti
određena CSS klasa nekom elementu.

Za ovu fazu primera možete pogledati (neznatno drugačiju) live aplikaciju ovde, a kod
možete preuzeti ovde.

Pregled dosadašnjih koraka u razvoju aplikacije:


 Kreirati projekat sa podrazumevanom aplikacijom.
 U glavnoj (root) komponenti promeniti vrednost title atributa.
 Dodati stilove u glavni (globalni) CSS fajl aplikacije (src/styles.css).
 Dodati preko CLI novu komponentu heroes (ng generate component heroes).
 Dodati selektor za novu komponentu unutar HTML šablona glavne komponente
 Kreirati Hero klasu u app direktorijumu.
 U heroes komponentu importovati klasu heroe.
 Kreirati fajl mock-heroes.ts koji sadrži konstantan niz HEROES, koji importuje klasu
heroe.
 U heroes komponentu importovati HEROES i u klasu dodati atribut heroes = HEROES.
 U HTML šablonu hero komponente dodati naslov i listu sa jednim elementom koji
prikazuje id i name za heroja.
 U početni tag elementa liste dodati Angular ‘repeater’ direktivu *ngFor.
 Dodati CSS stilove za listu i druge elemente.
 Dodati Angular event binding sintaksu za click događaj (naziv događaja u običnim
zagradama, znak jednakosti, i metoda koja se poziva pod znacima navoda sa
parametrom hero).
 U hero komponenti dodamo atribut selectedHero tipa Hero i metodu onSelect kojom
ovom atributu dodelјujemo objekat hero na koji je korisnik kliknuo.
 U HTML šablon hero komponente dodajemo prikaz detalјa (naslov, id, name), i to
tako da se ime prikazuje unutar input elementa primenom two-way data binding, a
kod naslova koristimo pipe za uppercase (velika slova). Koristimo selectedHero.
Takođe je potrebno u glavni modul importovati FormsModule (i navestu u imports).
 Dodajemo *ngIf direktivu za ceo blok u kome se prikazuju detalјi heroja, jer je
selectedHero nedefinisan atribut pre klika na nekog od heroja.
 Ako imamo posebnu klasu za odabranog heroja na čije ime je korisnik kliknuo,
dodelimo tu klasu odgovarajućem elementu liste ako je objekat hero u toj listi jednak
selectedHero.

Slika 6. Prva faza aplikacije.

Međutim, heroes komponenta prikazuje i listu heroja i detalјe o njima, što u budućnosti
može praviti problem pri dalјem razvoju. Cilј je da svaka komponenta ima specifične
zadatke, ne samo zbog lakšeg održavanja koda i timskog rada, već i zbog mogućnosti
ponovnog korišćenja bez potrebe za kopiranjem koda i sl.
Dakle, kreiraćemo komponentu hero-detail, i premestiti HTML kod za prikaz detalјa o heroju
iz HTML šablona komponente hero u šablon komponente hero-detail.
Potrebno je i da se uveze atribut Hero:
import { Hero } from '../hero';
U šablonu komponente heroes na mesto detalјa stavimo selektor heroe-detail komponente.
Novi šablon za hero-detail komponentu ne mora da prikazuje samo selectedHero, može da
prikazuje bilo kog heroja, pa ćemo svugde umesto selectedHero staviti hero.
Sada je potrebno povezati selectedHero iz heroes komponente i hero atribut koji se mapira
na hero atribut hero-detail komponente, što se postiže Angular property binding sintaksom
unutar selektora u kome se prikazuje šablon hero-detail komponente, tj. unutar app-hero-
detail kojoj dodajemo sintaksu za povezivanje:
<app-hero-detail [hero]="selectedHero"></app-hero-detail>
Za Angular property binding možete pogledati primere sintakse ovde.
U klasi HeroDetailComponent je potrebno deklarisati atribut hero tipa Hero, međutim,
potrebno je ispred deklaracije dodati dekorator @Input(), jer se sa ovim atributom povezuje
eksterna komponenta HeroesComponent (kod iznad sa <app-hero-detail>). Angular
kompajler će odbiti povezivanje sa atributom druge komponente osim ako je taj atribut
Input ili Output tipa, tj. ako ima odgovarajući dekorator. Takođe, ovde je potrebno dodati
importovati Input modul, ručno ili pomoću VSC, tako što se klikne na Input dekorator, potom
na „sijalicu) i odabere ponuđena opcija (kao na sledećoj slici):

Slika 7. Dodavanje import deklaracije za Input.

Na ovaj način se dobija aplikacija koja vizuelno isto izgleda i ima iste funkcionalnosti, ali smo
dodatno pojednostavili komponente, tako da npr. možemo dalјe unapređivati
HeroDetailComponent bez ikakvih izmena u HeroesComponent.
Finalan kod aplikacije je dat na strani predmeta pod nazivom Tour-of-heros (v2).zip

Faza 3 – Korišćenje servisa


Komponente ne treba da čuvaju direktno podatke niti treba da znaju da prikazuju lažne
podatke (mock-heroes.ts). I cilјu eliminisanja ovog problema kreiraćemo HeroService, servis
koji mogu koristiti sve klase aplikacije za dobijanje poadataka o herojima. Umesto klasičnog
instanciranja servisa pomoću klјučne reči new, koristimo DI (Dependency Injection), što je
maltene podrazumevani način kod Angular-a, kojim se praktično servis injektuje u
HeroesComponent konstruktor.
Takođe ćemo kreirati MessageService, koju HeroService koristi za slanje poruke, a
MessageComponent za prikaz poruke.
Kreiramo hero komandom:
ng generate service hero
Angular kreira osnovnu klasu servisa HeroService:
import { Injectable } from '@angular/core';

@Injectable({
providedIn: 'root'
})
export class HeroService {

constructor() { }
}
Injectable u import-u i kao dekorator označava da klasa učestvuje u DI, tj. da će HeroService
klasa pružiti injectable service, ali ćemo pokazati kako može imati i sopstvene injektovane
zavisnosti (injected dependency).
@Injectable dekorator prihvata objekat sa metapodacima za servis, kao @Component
dekorator sa klasama komponenti.
Kako bi HeroService servis bio dostupan DI pre nego što Angular može da injektuje servis u
HeroesComponent, potrebno je da se registruje provider, koji kreira i isporučuje servis, a u
ovom slučaju praktično instancira HeroService klasu kako bi omogućio servis. Zbog toga
HeroService mora da bude registrovan kao provajder servisa, za šta se koristi @Injectable
dekorator.
Angular podrazumevano registruje provajdera sa root injectior, što znači da je servis
omogućen na root nivou, pa se kreira jedna delјena instanca HeroService i injektuje u bilo
koju klasu gde se to traži. Registracijom provajdera preko @Injectable metapodataka
omogućava i da se aplikacija optimizuje izbacivanjem onih servisa koji se uopšte ne koriste.
Ako želimo da se provajder servisa registruje na drugom nivou, npr. ako želimo da servis
bude dostupan na nivou modula (npr. app), koristimo komandu:
ng generate service hero --module=app
HeroService može podatke preuzimati bilo odakle, sa veb servisa, iz lokalnog skladišta
podataka ili ih jednostavno može lažirati (mock). Izmeštanje manipulacije podacima iz
komponenti znači da bilo kada možemo promeniti implementaciju pristupa podacima bez
ikakvih izmena na komponentama. U dalјem ćemo i dalјe koristiti istu listu heroja.

Prvo u novi HeroService importujemo Hero i HEROES klase, a potom dodamo metodu
@Injectable({
providedIn: 'root'
})
export class HeroService {
getHeroes(): Hero[]{
return HEROES;
}
constructor() { }
}

Sada u HeroComponent importujemo HeroService:


import { HeroService } from '../hero.service';
a postojeću definiciju atributa heroes izmenimo:
heroes: Hero[];

Sada injektujemo heroService (tipa HeroService) kao parametar konstruktora:


constructor(private herService: HeroService) { }
Ovim se istovremeno definiše privatni atribut i praktično određuje gde će biti injektovan
HeroService. Kada Angular kreira HeroesComponent, DI sistem postavlјa heroService atribut
kao singleton instancu HeroService klase.
Sada u HeroesComponent dodamo funkciju koja od servisa dobija podatke o herojima:
getHeroes(): void {
this.heroes = this.heroService.getHeroes();
}
Sada treba pozvati metodu getHeroes(), što se može uraditi iz konstruktora. Međutim, takvu
praksu treba izbegavati, jer konstruktor treba da sadrži samo podatke o inicijalizaciji atributa
i sl. Zato ovu metodu stavlјamo unutar ngOnInit, što omogućava da Angular pozove ovu
metodu u pogodnom trenutku nakon kreiranja instance komponente.

Aplikacija funkcioniše, a na strani predmeta se može preuzeti kod za ovu fazu aplikacije pod
nazivom Tour-of-heros (v3).

Metoda HeroService.getHeroes() praktično radi sinhrono, tj. HeroService čita podatke


sinhrono, što ovde nije problem jer se čitaju lokalni podaci, pa bi isti efekat imali da smo
napisali:
this.heroes = this.heroService.getHeroes();
U praksi bi to bio problem, jer se podaci čitaju sa servera, što treba da bude asinhrona
operacija. Tada HeroService mora da čeka odgovor servera, pa getHeroes() ne može podatke
da vrati trenutno, što bi dovelo do blokiranja browser-a.
Dakle, HeroService.getHeroes() mora biti asinhrone prirode, a to se može izvesti na više
načina: callback funkcijom, Promise ili Obeservable.
Pošto će na kraju biti korišćena Angular HttpClient.get metoda za dobijanje podataka sa
servera, a ona vraća Observable, onda ćemo koristiti Observable.
Observable je jedna od klјučnih klasa u RxJS (Reactive Extensions for JavaScript) biblioteci.

Dakle, u hero.service importujemo Observable, kao i of simbole iz RxJS:


import { Observable, of } from 'rxjs';
Sada getHeroes() zamenimo sa:
getHeroes(): Observable<Hero[]>{
return of(HEROES);
}
U prethodnom kodu „of(HEROES)“ vraća Observable<Hero[]> kao jednu vrednost, niz heroja
iz mok fajla. Umesto toga, može se koristiti npr. HttpClient.get<Hero[]>() metoda, koja isto
vraća jednu vrednost.
Pošto metoda getHeroes sada vraća Obersvable, u HeroesComponent moramo izmeniti
getHeroes metodu tako da ima sledeći izgled:
getHeroes(): void {
this.heroService.getHeroes()
.subscribe(heroes => this.heroes = heroes);
}
Na ovaj način metoda getHeroes čeka da Observable pošalјe podatke, a potom subscribe
prosleđuje dobijeni niz objekata callback funkciji. Takođe, ovde se može ukloniti deklaracija
o importovanju HEROES.

Napomena: (x => this.x = x) je isto što i (function(x){this.x = x}).


Sada kreiramo MessagesComponent:
ng generate component messages
a potom izmenimo glavnu komponentu tako da prikaže šablon Messages komponente:

Potom kreiramo MessageService:


ng generate service message
i dodajemo dve metode, add() i clear() za manipulaciju kešom:
import { Injectable } from '@angular/core';

@Injectable({
providedIn: 'root'
})
export class MessageService {

messages: string[] = [];

add(message: string) {
this.messages.push(message);
}

clear() {
this.messages = [];
}
}

Sada importujemo ovaj servis u HeroService i potom injektujemo u konstruktor:


constructor(private messageService: MessageService) { }
i pošalјemo poruku kada su dobijeni podaci o herojima:
getHeroes(): Observable<Hero[]>{
this.messageService.add('HeroService: fetched heroes!');
return of(HEROES);
}
Sada treba prikazati poruku koja se dobija od HeroService, a koje prikazuje Messages
komponenta. Prvo je potrebno importovati MessageService u MessageComponent:
import { MessageService } from '../message.service';
i vršimo injektovanje MessageService preko konstruktora:
constructor(public messageService: MessageService) { }
Moramo koristiti public prefiks, jer Angular može da bind-uje samo javne atribute
komponente, a u sledećem koraku ćemo povezati ovaj atribut sa HTML šablonom.

Sada u MessageComponent šablonu (messages.component.html) dodamo sledeći kôd:


<div *ngIf="messageService.messages.length">

<h2>Messages</h2>

<button class="clear"
(click)="messageService.clear()">clear</button>
<div *ngFor='let message of messageService.messages'> {{message}}</div>
</div>

Na kraju, dodamo CSS kod za formatiranje bloka sa porukama.

Kod za ovu fazu aplikacije dostupan je na strani predmeta kao Tour-of-heros (v4).zip

You might also like