- Gaseste fisierul
app.component.html
- Editeaza-l astfel incat sa afiseze
M-ai editat, deci se dezlantuie magia
in loc demy app
Gaseste fisierul HTML in care AppComponent este reprezentat printr-un tag HTML.
Hint: AppComponent defineste tag-ul HTML in decoratorul @Component
Angular ne ofera o sintaxa speciala pentru a include in template (in HTML) date definite in logica componentei (in fisierul .ts)
// Component Typescript code
myName = 'Angular Wiz Escu'
// Component template HTML code
<div> Numele meu este {{ myName }} </div>
// Result
Numele meu este Angular Wiz Escu
- Deschide template-ul componentei AppComponent (
app.component.html
) - Inlocuieste textul din task-ul anterior cu proprietatea
name
- Deschide typescript-ul componentei AppComponent (
app.component.ts
) - Schimba valoarea proprietatii
name
: observa ca se actualizeaza si pagina
Hint: Nu uita sa folosesti interpolarea ({{name}}
). Altfel va fi afisat string-ul name
in loc de valoarea lui
In componenta AppComponent am adaugat proprietatea destination
ca obiect.
Observati structura.
- Deschide template-ul componentei AppComponent
- Inlocuieste campurile care definesc destinatia cu proprietatile obiectului
destination
folosind interpolare. - Modifica informatii din obiectul
destination
si observa cum se actualizeaza in pagina.
Hint: {{destination.name}}
acceseaza proprietatea name
din obiectul destination
Din moment ce orice site de calatorii are mai mult de o destinatie, am adaugat un array de destinatii in AppComponent. Observati structura.
Proprietatea destination
este primul element din array-ul destinations
.
Dorim sa afisam toate destinatiile din array.
O varianta ar fi copy/paste al template-ului care defineste destinatia si folosirea unei variable pentru fiecare destinatie.
Angular ofera suport pentru modificarea structurii template-ului (in cazul acesta duplicarea unor elemente din template) cu ajutorul directivelor structurale.
*ngFor
va repeta blocul HTML pe care este aplicat pentru fiecare element dintr-o colectie.
// Component Typescript code
elements = ['one', 'two', 'three']
// Component template HTML code
<div *ngFor="let element of elements"> Element: {{ element }} </div>
// Result
Element: one
Element: two
Element: three
- Deschide template-ul componentei AppComponent
- Adauga directiva
*ngFor
astfel incat sa fie afisate toate destinatiile din array-uldestinations
Hint: Nu uita sa folosesti (*
) in fata lui ngFor
. Aceasta sintaxa indica faptul ca este o directiva structurala: <div class="destination" *ngFor="let destination of destinations">
*ngIf
controleaza afisarea unui element HTML, in functie de o conditie.
// Component Typescript code
vreau = true;
// Component template HTML code
<p> Nu <span *ngIf="vreau">, </span> vreau sa invat Angular </p>
// Result
Nu, vreau sa invat Angular
- Deschide template-ul componentei AppComponent si sterge comentariile pentru
div
-ul cu clasadisplay-as-list
.- Hint: In HTML orice se afla intre
<!--
si-->
este comentat.
- Hint: In HTML orice se afla intre
- Adauga directiva
*ngIf
astfel incat destinatiile sa fie afisate doar sub forma de lista. VariabilaisList
reprezinta conditia de afisare.
In template ajunge, de multe ori, continut dinamic. Acesta, sau o parte din acesta, are nevoie de formatare.
De exemplu, vreau ca un text adaugat prin interpolare in template, sa inceapa intotdeauna cu prima litera mare, sau vreau sa fie tot textul uppercase, sau textul este un numar si vreau sa aiba maxim doua zecimale etc.
Pipe-urile sunt o metoda buna de a formata string-uri, sume valutare, date de calendar etc. Angular are pipe-uri predefinite (ex. titlecase
) dar se pot crea si pipe-uri custom.
Sintaxa: {{ elementToTransform | pipeName }}
Exemplu:
// Component Typescript code
name = 'darth vader'
// Component template HTML code
<p> The hero's name is {{ name | titlecase }} </p>
// Result
The hero's name is Darth Vader
- Deschide template-ul componentei AppComponent
- Adauga pipe-ul
currency: USD
pentrudestination.price
si sterge currency-ul adaugat static ($
)
Hint: Nu uita sa folosesti |
dupa destination.price
. Aceasta sintaxa indica faptul ca se foloseste un pipe.
Un event binding este o metoda prin care putem executa cod Javascript atunci cand se declanseaza evenimentul.
Sintaxa: (event)="statement"
Exemplu:
// Component Typescript code
deleteProduct() {
console.log('Product is being removed.');
}
// Component template HTML code
<button (click)="deleteProduct()">Delete product</button>
Ele reprezinta modul prin care actualizezi starea aplicatiei in urma unei actiuni.
Intre ghilimele se pune un template statement (cod javascript sau apel de functie).
- Deschide template-ul componentei AppComponent
- Gaseste butonul cu textul "List/Cards"
- Conecteaza evenimentul
click
la metodatoggleDisplay
pentru a schimba afisarea destinatiilor- Hint foloseste proprietatea
isList
in metodatoggleDisplay
- Hint foloseste proprietatea
- Folosind interpolarea, afiseaza butonul cu textul "List" atunci cand destinatiile sunt afisate sub forma de cards si cu textul "Card" atunci cand destinatiile sunt afisate sub forma de lista.
- Hint Foloseste conditional (ternary) operator in interpolare
// HTML <p>{{ isTrue ? 'Adevarat' : 'Fals' }} </p> // Rezultat cand isTrue === true <p> Adevarat </p>
- Hint Foloseste conditional (ternary) operator in interpolare
!Info: Ce este un card?
Card-urile sunt blocuri mici, similare ca design, dar cu continut diferit. Un card poate avea orice tip de continut - imagini, text, link-uri etc.
Componentele reprezinta modul in care Angular organizeaza si incapsuleaza (izoleaza) continutul.
- HTML - structura si continut
- CSS - stil
- Typescript - logica
In folderul src/app
au fost create urmatoarele fisiere:
- favorite-destination.component.css
- favorite-destination.component.html
- favorite-destination.component.ts
- Decomenteaza codul care ii spune lui Angular ca
FavoriteDestinationComponent
este o componenta.- Q: Care sunt informatiile specificate in decoratorul @Component si de ce sunt necesare?
- Q: Sunt toate necesare?
- Afiseaza componenta
FavoriteDestinationComponent
ca si copil al luiAppComponent
in locul indicat de comentariu.- Foloseste selectorul componentei
FavoriteDestinationComponent
ca un tag HTML (cum este folosit selectorul luiAppComponent
inindex.html
) - Daca ai obtinut Eroarea
'app-favorite-destination' is not a known element
inseamna ca ai reusit - Q: De ce avem aceasta eroare?
- Foloseste selectorul componentei
- Asculta indemnul erorii.
- Hint: Exista vreo componenta pe care o putem folosi ca exemplu pentru a rezolva eroarea?
- Adauga o proprietate
isFavorite
in clasaFavoriteDestinationComponent
si afiseaza o stea plina doar candisFavorite
este true.- Hint vezi conditional (ternary) operator de la task-ul din capitolul anterior
- Q: de ce am folosit ghilimele pentru 'star' si 'star_outline'
Elementele HTML sunt configurate prin atribute HTML care se mapeaza pe proprietati din Javascript.
// HTML
<a title="Enteresant" href="#"> Un link interesant </a>
// Javascript
console.log(elementA.title)
// Console output
Enteresant
Trimiterea de date catre componente se face in acelasi mod, prin proprietati. Proprietatile marcate cu decoratorul @Input()
vor putea fi folosite pentru a configura componente.
// Typescript
public class TheAnswerComponent {
@Input() question;
}
// HTML
<app-the-answer question="meaning of life"></app-the-answer>
Componenta FavoriteDestinationComponent
are proprietatea isFavorite
.
- Marcheaza proprietatea ca Input
- Hint: Decoratorul
Input
face parte din@angular/core
- Hint: Decoratorul
- Din
app.component.html
, configureaza proprietatea cu true, apoi cu false- Q: Se modifica afisarea din componenta copil? Dar daca folosim sintaxa de binding
[isFavorite]
?
- Q: Se modifica afisarea din componenta copil? Dar daca folosim sintaxa de binding
- Folosind data binding, legati proprietatea
isFavorite
dedestination.isFavorite
Elementele HTML emit evenimente. Mai devreme ne-am abonat la evenimentul (click)
al butonului 'Display as' cu functia toggleDisplay()
.
Componentele Angular pot face acelasi lucru: sa emita evenimente si ne abonam la aceste evenimente folosind aceeasi sintaxa.
Vom modifica componenta FavoriteDestinationComponent
astfel incat sa emita evenimentul favClick
cand este clickaita stelutza.
- Adauga proprietatea
favClick
de tip@Output
in componentaFavoriteDestinationComponent
- Hint:
@Output() favClick = new EventEmitter();
- Q: all good? Poate ar trebui sa importam directiva
Output
de undeva? - Q: now? Developer tools ce zice? (Hit F12)
- Hint:
- Emite evenimentul
favClick
atunci cand se face click pe steluta- Hint:
(click)="favClick.emit()"
- Hint:
- In
AppComponent
aboneaza-te la evenimentulfavClick
al componenteiFavoriteDestinationComponent
. Schimba valoarea proprietatiidestination.isFavorite
de fiecare data cand se emite favClick.- Hint:
destination.isFavorite = !destination.isFavorite
- Hint:
Creaza o componenta care sa incapsuleze afisarea destinatiei ca si card.
ng generate component destination-details
- Muta HTML-ul care afiseaza destinatia ca si card in template-ul noii componente
- Hint: Urmareste comentariile din app.component.html
- Adauga selectorul componentei
DestinationDetailsComponent
in template-ul luiAppComponent
, in locul unde era inainte cardul- Hint:
<app-destination-details *ngIf="!isList"></app-destination-details>
- Q: Ce se intampla daca nu folosesc directiva
*ngIf
? - Q: Observa si explica eroarea din developer tools (F12)
TypeError: Cannot read property 'name' of undefined
- Hint:
- Adauga o proprietate de tip
@Input
in componentaDestinationDetailsComponent
- Hint:
@Input() destination;
- Hint:
- Din template-ul lui
AppComponent
transmitedestination
catre app-destination-details folosind property binding:- Hint:
<app-destination-details [destination]="destination" *ngIf="!isList"></app-destination-details>
- Q: De ce nu s-a aplicat stilizarea configurata in
app.component.css
in componenta copilDestinationDetailsComponent
?
- Hint:
- Muta stilizarea din
app.component.css
indestination-details.component.css
- Hint: Urmareste comentariile din
app.component.css
- Hint: Urmareste comentariile din
Folosind pasii de mai sus, extrage codul pentru afisarea destinatiei ca lista intr-o componenta denumita destination-summary
Spre deosebire de componente, serviciile sunt create o singura data si sunt disponibile pe toata durata de viata a aplicatiei.
Un exemplu ar fi starea aplicatiei. Destinatiile disponibile ar putea fi luate de pe server si stocate intr-un serviciu. Orice componenta care are nevoie de destinatii poate sa le acceseze prin serviciu, fara sa ceara din nou de la server destinatiile.
In Angular, un serviciu este disponibila pentru orice parte din aplicatie folosind sistemul Dependency Injection al Angular-ului.
Pentru aplicatia de calatorii, destinations.service.ts
este locul unde "stocam" datele pentru destinatii.
- Observatie: toate fisierele de tip serviciu au in denumirea 'service'; ex:
nume-serviciu.service.ts
import { Injectable } from '@angular/core';
// dependency injection decorator
@Injectable({
providedIn: 'root' // singleton
})
export class DestinationsService {
constructor() { }
}
- Muta
destinations
din AppComponent in DestinationsService - Injecteaza serviciul in AppComponent:
- Hint: Decomenteaza din constructorul lui AppComponent
destinationsService: DestinationsService
.
- Hint: Decomenteaza din constructorul lui AppComponent
- Foloseste datele din serviciu pentru a popula aplicatia cu destinatiile de calatorie
Ce este?
Daca vrem sa afisam continut in functie de URL, putem sa folosim functionalitatile de rutare Angular (Angular router).
Angular router te ajuta sa afisezi componentele in functie de locul in care acesta se afla in aplicatie (URL).
Router-ul permite navigarea dintr-un view (pagina/componenta) catre alt view pe baza actiunilor utilizatorului:
- scrierea unui URL in bara de adrese
- click pe link-uri din pagina
- click pe butoanele browser-ului Inainte si Inapoi
Sintaxa pentru a defini rutele din aplicatie: RouterModule.forRoot([...])
.
Modulul de rutare (router) trebuie importat in modulul care il foloseste (in cazul nostru app.module.ts
):
import { RouterModule } from '@angular/router';
@NgModule({
imports: [
...
RouterModule.forRoot([
{ path: 'dashboard', component: DashboardComponent },
// for 'klm-training' in URL the KlmTrainingComponent will be loaded;
// eg: https://address/klm-training
{ path: 'klm-training', component: KlmTrainingComponent },
// for empty pathname you will be redirected to '/dashboard' wich will load DashboardComponent
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' }
])
...
]
Pe langa modulul de rutare avem nevoie ca elementele din HTML sa solicite router-ului incarcarea unui URL: <a routerLink="dashboard"> Dashboard </a>
.
Aplicatia are acum urmatoarea structura:
- AppComponent este componenta parinte si afiseaza
- Bara de navigare
- DestinationsComponent
- DestinationsComponent afiseaza destinatiile ca si card-uri sau lista infunctie de butonul Display as
- ContactComponent nu este afisata momentan
La finalul task-ului AppComponent:
- Afiseaza intotdeauna bara de navigare
- Afiseaza DestinationsComponent sau ContactComponent in functie de URL folosind Angular routing
- Decomenteaza, modulul de rutare in
app.module.ts
- Adauga inca o ruta catre pagina contact
- Adauga
routerLink
catre cele doua componente anchor (<a>
) in bara de navigare a aplicatiei- Hint:
<a routerLink="destinations">
- Q: Apasa butoanele Destinations si Contact din bara de navigare. Ce se intampla? Dar in bara de adrese se modifica ceva?
- Q: De ce nu se modifica continutul paginii?
- Hint:
- Afiseaza in template-ul lui AppComponent view-urile rutate de router
- Hint: Tag-ul
<router-outlet>
(directiva importata odata cuRouterModule
) spune router-ului unde sa afiseze view-urile rutate. - Hint: Template-ul lui AppComponent nu mai are nevoie sa afiseze DestinationsComponent folosind tag-ul
<app-destinations>
deoarece aplicatia va afisa aceasta componenta doar atunci cand user-ul va naviga catre ea - Q: Ce se intampla daca folosim butoanele Back si Forward ale browser-ului?
- Hint: Tag-ul
- Adauga
routerLinkActive
in tag-urile de navigare<a>
:<a routerLink="destinations" routerLinkActive="active">
- acelasi lucru si pentru contact
- Q: ce s-a schimbat in afisare? Cum?
- Q: Deschide devtools (F12) si observa elementele
<a>
. Click pe link-uri. Ce se intampla?
Angular ne pune la dispozitie mai multe metode de a comunica cu componentele:
- @Input: datele sunt trimise catre componenta
- @Output: datele sunt trimise dinspre componenta (ca evenimente)
- Two way binding: datele sunt tinute in-sync (ngModel)
ngModel ne ajuta sa sincronizam valoarea unui input din HTML cu o proprietate din logica componentei.
// Typescript
name = 'Zorro';
// HTML
<input [(ngModel)]="name"> // Banana in a box syntax
Componenta Contact
are doua input-uri folosite la adaugarea unui nou testimonial.
- Gaseste input-urile
- Adauga banane in cutie ca sa poti pune un nou testimonial
- Observa sincronizarea live
- Navigheaza in pagina
Destinatii
, apoi revino in paginaContact
- Ce s-a schimbat? De ce?
- Cum oprim schimbarea? :)