Aplicațiile Web deobicei nu sunt compuse dintr-o singură componentă, ci din mai multe componente care toate impreună îndeplinesc funcționalitatea noastră. În general, se obișnuiește organizarea aplicației într-un arbore de componente imbricate:
Deobicei avem o coponenta parinte, care reprezintă însăși pagina, și ea include o serie de componente copii, care la rândul său pot avea alți copii.
Spre exemplu, am putea avea componente pentru header, sidebar și content area, fiecare conținând, de obicei, alte componente pentru blog posts, link-uri de navigare ș.a.
Aceste componente trebuie să comunice intre ele pentru a oferi o funcționalitate integrată. Avem 2 direcții de comunicare:
De la părinte spre copil - componenta parinte transmite informație catre o componentă copil
De la copil spre părinte - componenta copil informează părintele că au intervenit modificări
Proprietăți (comunicarea părinte - copil)
Să presupunem că vrem să implementăm o pagină, care ar avea o listă de comentarii, și fiecare comentariu are text, un autor, si un număr like-uri care le-a primit.
O structură clasică a acestei funcționalități ar fi o componentă care reprezintă un comentariu, și o componentă părinte - pagina. Pagina va include o listă de componente Comment în ea.
Desigur comentariile vor fi păstrate în pagina părinte, ca o listă de obiecte, respectiv avem nevoie de un mecanism să transmitem către elementul copil informația necesară.
Proprietăți simple
Pentru a defini o componentă care acceptă o proprietate, folosim attributul props în declararația JavaScript. Astfel acel prop poate fi folosit în mod similar ca și data
Pentru fiecare proprietate a componentei e recomandabil să setăm tipul de date care-l așteptăm. În acest caz - String
Pentru a folosi această componentă, doar îi transmitem proprietatea cerută când o declarăm:
<template>
<div>
<h1>My Comments</h1>
<Comment text="This is my first Comment!" />
<Comment text="This is my second Comment!" />
</div>
</template>
<script>
import Comment from "./components/Comment";
export default {
components: {
Comment
}
...
};
</script>
E important să importăm componenta folosită, și să o declarăm în components
Proprietăți dinamice
Desigur, nu e cel mai practic să transmitem un text static. Vue permite să transmitem valori dinamice către sub-componente în mod similar cum transmitem și pentru atribute standarte html
<template>
<div>
<h1>My Comments</h1>
<Comment v-for="c in comments" :text="c" />
</div>
</template>
<script>
import Comment from "./components/Comment";
export default {
components: {
Comment
},
data: function() {
return {
comments: [
"The first comment, which is very much awesome",
"The second comment long comment that makes more sense in my head"
]
};
}
};
</script>
CamelCase vs kebab-case
În programare avem câțiva termeni care definesc convenții de unire a mai multor cuvinte într-o singură expresie, 2 din ele:
CameCase - înseamnă că fiecare cuvânt nou din expresia noastră va începe cu literă mare. Toate și funcțiile în JavaScript, spre exemplu, e recomandat să respecte această convenție
kebab-case - înseamnă că unim cuvintele în expresia noastră cu -, și toate sunt cu litere mici. Un exemplu unde se utilizează această convenție sunt atributele în html
Având această diferență de convenții între html și JavaScript, Vue a adoptat o regulă simplă: Definim proprietățile în JavaScript sub convenția CamelCase, iar când le transmitem din html, le transmitem sub kebab-case, și el va avea grijă să știe care și cui aparține:
Array-ul de comentarii deja sunt obiecte, cu mai multe atribute în ele, și am inclus și un id, care îl folosim ca :key în v-for
Evenimente (comunicarea copil - părinte)
Pe parcursul anilor de dezvoltare a tehnologiilor web, au fost învățate o serie de lecții și practici care par să fie cele mai bune. Una din aceste practici, este ideea de direcția de parcurgere a datelor.
În termeni simpli, toată ideea se bazeaza pe faptul că componentele copii primesc direct datele de la componente părinți prin proprietăți, dar nu pot modifica direct datele componentelor parinti. Dacă vor să comunice o schimbare, trebuie să emită un eveniment.
O altă concluzie a acestei practici este că interpretăm toate datele care le primim prin props ca date nemodificabile. Asta inseamnă că nu putem să le folosim direct în forme prin v-model, dacă vrem să le includem intr-o formă, le copiem întrun datalocal, și îl folosim pe acela.
Extindem un pic lista noastră de comentarii, și adăguăm posibilitatea ca oamenii să dea like la fiecare comentariu. De asemenea vrem sa metinem un contor cu numărul total de like-uri care le-am primit:
Modificarea internă a valorilor
Pentru a implementa funcționalitatea de a da like la fiecare comentariu, trebuie să adăugăm un buton care crește acel numă la fiecare click. Din cauza că nu putem modifica direct props care le primim, trebuie să copiem valoarea care vine într-o variabilă locală a componentei:
Observați că likesCount nu se modifică, doar copiem valoarea în internalLikes și mai departe lucrăm doar cu ea.
În acest moment, fiecare număr din fiecare comentariu poate fi crescut, doar că numărul total de comentarii din pagina părinte nu se modifică, trebuie cumva să-i transmitem informația când se întâmplă un like.
Emiterea evenimentelor proprii
Pentru a transmite din componenta copil către componenta părinte informația că a avut loc un like, trebuie să emitem un eveniment. Pentru asta trebuie să adaugăm o singură linie de cod în metoda likeComment():
Ca date într-un eveniment putem transmite atât tipuri simple de date (numere, string), cât și obiecte complexe, cum in cazul de mai sus - un obiect cu proprietatea likeDelta
Deobicei e recomandat să transmitem obiecte cu proprietăți, pentru a fi mai expliciți care e informația transmisă.
Pentru a prelucra aceste evenimente în componenta părinte trebuie să modificăm un pic prelucrarea pentru a primi evenimentul cu date:
Observați că în template transmitem variabila $event când apelăm metoda onLikeChange($event). această variabilă e automat disponibilă și conține datele care au fost transmite de componenta Comment
De asemenea la declarația metodei în javascript am definit un parametru de intrare event- care îl folosim pentru a modifica valoarea lui totalLikes
Exemplul complet
Astfel am implementat complet comunicarea inversă de la copil către părinte prin evenimente cu date.