Componentele Ember o scufundare profundă

Ember.js este un cadru MVC JavaScript care permite dezvoltatorilor să creeze aplicații web ambițioase. Deși MVC-ul pur permite unui dezvoltator să-și rezolve problemele, nu vă oferă toate instrumentele și aplicația dvs. va avea nevoie de alte construcții. Astăzi, voi vorbi despre unul dintre aceste construcții. Componentele ember sunt în esență bucăți reutilizabile de UI. Dacă nu sunteți familiarizat cu Ember, vă rugăm să consultați Noțiuni introductive cu Ember.js sau Let's Learn Ember Curs. În acest tutorial vom acoperi specificațiile Web Components, vom învăța cum să scriem o componentă în Ember, să vorbim despre compoziție, să explicăm diferența dintre o vizualizare Ember și o componentă Ember și să practicăm integrarea pluginurilor cu componente Ember.


Un cuvânt despre componentele Web

Componentele Ember se bazează pe specificația W3C Web Components. Specificația este compusă din patru specificații mai mici; șabloane, decoratori, umbra DOM și elemente personalizate. Dintre aceste patru concepte, doar trei dintre ele au specificații mai stricte, decoratorii fiind excepția. Având specificațiile existente, dezvoltatorii de cadre au reușit să completeze aceste noi interfețe API înainte de a fi implementate de furnizorii de browsere.

Există câteva concepte importante care trebuie înțelese atunci când vorbești despre componente:

  • Componentele nu știu nimic despre lumea exterioară decât dacă au trecut în mod explicit
  • Componentele ar trebui să aibă o interfață bine definită cu lumea exterioară
  • Componentele nu pot manipula orice JavaScript în afara componentei
  • Componentele pot difuza evenimente
  • Elementele personalizate trebuie să fie numite cu o cratimă
  • Afară JavaScript nu poate manipula componente

Componentele web furnizează o adevărată încapsulare pentru widget-urile UI. Mai jos este o diagramă a modului în care o componentă funcționează la nivelul cel mai de bază.

În timp ce Ember a umplut cu succes o mulțime de specificații, cadre precum AngularJS, Dart, Polymer și Xtags au soluții similare. Singurul avertisment aici este că Ember și Angular în prezent nu au stiluri de aplicare a componentei. Cu timpul, aceste soluții polyfill vor dispărea, iar cadrele vor adopta implementarea furnizorului de browser. Aceasta este o abordare fundamental diferită a dezvoltării, deoarece putem profita de specificațiile viitoare fără a ne lega de caracteristicile experimentale în browsere.


Componenta Ember cea mai de bază

Cu ajutorul cunoștințelor noastre despre Componentele Web, să implementăm componenta foarte bazată pe numele meu de sus, dar în Ember. Să începem prin descărcarea Kitului Starter Ember de pe site-ul Ember. În momentul acestui tutorial, versiunea Ember este 1.3.0. Odată ce ați descărcat fișierele deschise în editorul dvs. preferat, ștergeți toate șabloanele din index.html (marcat cu nume de date-șablon) și totul în app.js.

Primul lucru pe care vom dori să-l facem este să creăm șablonul pentru componente. De dragul acestui tutorial vom folosi șabloane inline. Faceți acest lucru scriind următoarele în dumneavoastră index.html fişier. De asemenea, trebuie să creați o nouă aplicație Ember în JavaScript.

   var App = Ember.Application.create ();

Veți observa că numele-șablon de date are un nume de cale în locul unui șir simplu. Motivul pentru care prefixăm numele componentei noastre "componente/" este să-i spunem lui Ember că avem de-a face cu un șablon de componente și nu cu un șablon regulat de aplicare. Veți observa, de asemenea, că numele componentei are liniuța în ea. Este vorba despre dimensiunile de nume pe care le-am menționat în specificația Web Components. Păstrarea numelor se face astfel încât să nu avem coliziuni de nume cu etichetele existente.

Dacă deschidem browser-ul, nu ar trebui să vedem nimic diferit. Motivul pentru aceasta este că încă nu am plasat nimic în șablonul meu de nume. Să avem grijă de asta.

... 

Acum, în browser trebuie să vedeți ceva asemănător imaginii de mai sus. Încă nu suntem terminați, după cum puteți vedea că de fapt nu tipăm un nume. După cum am menționat în prima secțiune, componentele ar trebui să expună o interfață bine definită la lumea exterioară. În acest caz, suntem preocupați de nume. Deci, să trecem în numele prin plasarea unui atribut name pe componenta mea-nume.

... 

Când actualizați pagina pe care ar trebui să o vedeți "Bună, numele meu este Ciad". Toate acestea cu scrierea unei linii de JavaScript. Acum, că avem o senzație de a scrie o componentă de bază, să vorbim despre diferența dintre componentele Ember și viziunile Ember.


Componentele Ember vs. Vizualizări extraordinare

Ember este un MVC, astfel încât unii ar putea să se gândească: "De ce să nu folosiți doar o viziune pentru asta?" Aceasta este o întrebare legitimă. Componentele sunt de fapt o subclasă a lui Ember.View, cea mai mare diferență aici este că vederile sunt în general găsite în contextul unui controler. Luați exemplul de mai jos.

 App.IndexController = Ember.Controller.extend (myState: 'on'); App.IndexView = Ember.View.extend (click: function () var controler = this.get ('controller'), myState = controller.get ('myState' instanța console.log (myState) // șirul "on");
 

Vizualizările se situează în mod normal în spatele unui șablon și se transformă într-o acțiune semantică (openMenu, editName, hideModal, etc.) într-un controler sau într-un traseu (input, click, mouseEnter, mouseMove etc.). Un alt lucru de subliniat este că șabloanele au nevoie și de un context. Deci, ceea ce se întâmplă până la urmă este că Ember trasează contextul prin convențiile de denumire și adresa URL. Vedeți diagrama de mai jos.

După cum puteți vedea, există un nivel de ierarhizare bazat pe adresa URL și fiecare nivel al acestei ierarhii are propriul context care este derivat prin convențiile de numire.

Componentele ember nu au un context, știu doar despre interfața pe care o definesc. Acest lucru permite ca o componentă să fie redată în orice context, făcându-l decuplată și reutilizabilă. Dacă componenta expune o interfață, este sarcina contextului de a îndeplini această interfață. Cu alte cuvinte, dacă doriți ca componenta să fie afișată corect, trebuie să o furnizați cu datele pe care le așteaptă. Este important să rețineți că aceste valori transmise pot fi atât șiruri, cât și proprietăți legate.

Atunci când proprietățile legate sunt manipulate în interiorul unei componente, aceste modificări sunt încă propagate ori de câte ori acestea sunt menționate în aplicația dvs. Acest lucru face ca componentele să fie extrem de puternice. Acum, că avem o bună înțelegere a modului în care componentele sunt diferite de opiniile, să analizăm un exemplu mai complex care ilustrează modul în care un dezvoltator poate compune mai multe componente.


Componența componentelor

Un lucru foarte frumos despre Ember este că este construit pe concepte de ierarhie a UI și acest lucru este foarte evident cu compoziția componentelor. Mai jos este un exemplu despre ceea ce vom face. Este un simplu interfață de chat de grup. Evident, nu am de gând să scriu un întreg serviciu de chat pentru a porni UI-ul, dar putem vedea cum putem rupe UI-ul în componente reutilizabile și compuse.

Să aruncăm o privire mai întâi cum vom distruge UI-ul în părți mai mici și mai digerabile. Orice putem scoate o cutie în jurul nostru este o componentă, cu excepția textului și a intrărilor de butoane din partea inferioară a interfeței UI. Scopul nostru este acela de a putea configura doar componenta la stratul exterior, altceva ar trebui să funcționeze.

Să începem prin crearea unui nou fișier html numit chat.html și stabilirea tuturor dependențelor pentru Ember. Apoi creați toate șabloanele.

       

Veți vedea că componentele pot fi imbricate în interiorul altor componente. Acest lucru face ca componentele ca lego-urile să le putem asambla în orice fel dorim. Trebuie doar să scriem pe interfața componentei.

Dacă mergem acum să privim în browser, nu ar trebui să vedem prea multe, pentru că nu avem date care să curgă în componentă. Veți observa că, deși nu există date, componentele nu aruncă o eroare. Singurul lucru pe care îl obțineți aici este zona de intrare și butonul de expediere. Acest lucru se datorează faptului că acestea nu depind de ceea ce a trecut.

Privind puțin mai mult asupra șabloanelor, veți observa că am atribuit câteva lucruri componentei chat-grup.

 

În acest caz, trecem modelul din contextul IndexRoute ca "mesaje" și am setat șirul de Trimite mesaj ca acțiune pe componentă. Acțiunea va fi utilizată pentru difuzare atunci când utilizatorul dorește să trimită un mesaj nou. Vom acoperi acest lucru mai târziu în tutorial. Celălalt lucru pe care îl veți observa este că ne confruntăm cu interfețe stricte cu componentele imbricate, toate folosind datele transmise din interfața de grup-chat.

... 
    #aceste mesaj în mesaje
  • chat-mesaj username = message.twitterUserName mesaj = message.text time = message.timeStamp
  • /fiecare
...

Așa cum am menționat mai înainte, puteți transfera șiruri sau proprietăți legate în componente. De regulă, folosiți citate atunci când treceți un șir, nu utilizați citate atunci când treceți o proprietate limitată. Acum, când avem șabloanele noastre, să aruncăm niște date martor la ea.

 App = Ember.Application.create (); App.IndexRoute = Ember.Route.extend (model: function () return id: 1, firstName: 'Tom', lastName: 'Dale', twitterUserName: 'tomdale' Tomster a fost minunat. ", timeStamp: Date.now () - 400000,, id: 2, firstName: 'Yehuda', lastName: 'Katz', twitterUserName: 'wycats' o idee bună. ', timeStamp: Date.now () - 300000,];);

Dacă mergem să privim acest lucru în browser acum, ar trebui să vedem un pic de progres. Dar mai sunt încă unele lucruri de făcut, în principal obtinerea imaginilor pentru a apărea, formatarea datei și posibilitatea de a trimite un mesaj nou. Să avem grijă de asta.

Cu componenta noastră de utilizator-avatar, dorim să folosim un serviciu numit Avatars.io pentru a prelua un avatar pe Twitter pe baza numelui de utilizator Twitter. Să analizăm modul în care componenta utilizator-imagine este utilizată în șablon.

  

Este o componentă destul de simplă, dar veți observa că avem o proprietate legată numită avatarUrl. Va trebui să creați această proprietate în cadrul JavaScript pentru această componentă. Un alt lucru pe care îl veți observa este că specificăm serviciul de la care vrem să preluăm avionul. Avatars.io vă permite să obțineți avatare sociale de pe Twitter, Facebook și Instagram. Putem face această componentă extrem de flexibilă. Să scriem componenta.

 App.UserAvatarComponent = Ember.Component.extend (avatarUrl: funcție () var username = this.get ('username'), service = this.get (' , 'instagram'], dacă availableServices.indexOf (service)> -1) return 'http://avatars.io/' + service + '/' + username; return 'images / cat.png' .property ("nume de utilizator", "serviciu"));

După cum puteți vedea, pentru a crea o nouă componentă, pur și simplu urmați convenția de numire a NAMEOFCOMPONENTComponent și extinde Ember.Component. Acum, dacă ne întoarcem la browser, ar trebui să vedem acum avatarele noastre.

Pentru a avea grijă de formatarea datei, hai să folosim moment.js și să scriem un ajutor pentru Handlebars pentru a formata data pentru noi.

 Ember.Handlebars.helper ('format-date', funcție (dată) moment de întoarcere (data) .fromNow (););

Acum, tot ce trebuie să facem este să aplicăm ajutorul pentru componenta noastră de timbru.

 

Ar trebui să avem acum o componentă care să formateze datele în locul timestampurilor epocii Unix.

Cu toate acestea putem face mai bine. Aceste timestamps-uri ar trebui să se actualizeze automat în decursul timpului grosier, așa că facem ca componenta noastră de timbru să facă exact asta.

 App.TimeStampComponent = Ember.Component.extend (startTimer: function () var actualTime = this.get ('time'); this.set ('time', currentTime - 6000); this.scheduleStartTimer ()) .on ('didInsertElement'), killTimer: function () Ember.run.cancel (this._timer) ; .on ("willDestroyElement"));

Câteva puncte pe care să le menționăm aici. Unul este pe() sintaxă declarativă de manipulare a evenimentului. Acest lucru a fost introdus în Ember înainte de lansarea 1.0. Ea face exact ceea ce credeți că o face, când componenta temporală este inserată în DOM, scheduleStartTime se numește. Când elementul urmează să fie distrus și curățat killTimer se va numi metoda. Restul componentei spun doar timpul să se actualizeze la fiecare minut.

Celălalt lucru pe care îl veți observa este că există mai multe apeluri către Ember.run. În Ember există un sistem de așteptare, denumit în mod obișnuit buclă de alergare, care devine flushed atunci când datele sunt schimbate. Acest lucru se face pentru a coaliza în mod substanțial schimbările și a face schimbarea o singură dată. În exemplul nostru vom folosi Ember.run.later pentru a rula startTimer fiecare minut. Vom folosi de asemenea Ember.run.cancel pentru a declasa temporizatorul. Aceasta este, în esență, propriile metode de pornire și oprire ale lui Ember. Ele sunt necesare pentru a menține sistemul de așteptare în sincronizare. Pentru mai multe despre bucla de alergare vă sugerăm să citiți articolul lui Alex Matchneer "Tot ce nu ați vrut să știți despre buclă de alergare".

Următorul lucru pe care trebuie să-l facem este să configurați acțiunea astfel încât atunci când utilizatorul să apară trimiterea, va fi creat un nou mesaj. Componenta noastră nu ar trebui să aibă grijă de modul în care datele sunt create, ar trebui doar să difuzeze că utilizatorul a încercat să trimită un mesaj. Al nostru IndexRoute va fi responsabil pentru luarea acestei acțiuni și transformarea în ceva semnificativ.

 App.GroupChatComponent = Ember.Component.extend (mesaj: ", acțiuni: submit: function () var message = this.get ('mesaj'). [0]; // Returnează valoarea acțiunii // și trimite acțiunea cu mesajul this.sendAction ('acțiune', mesaj); / / Când se execută buclă de alertă Ember // parcurgeți până la partea de jos Ember. run.schedule ('afterRender', funcția () conversation.scrollTop = conversation.scrollHeight;); // Resetați câmpul cu mesaje text this.set ('message', '););
 
input type = "text" placeholder = "Trimiteți un mesaj nou" value = message input type = "submit" value = "Trimite"

Deoarece componenta de grup-chat deține butonul de intrare și trimitere, trebuie să reacționăm la utilizator făcând clic pe trimiteți la acest nivel de abstractizare. Când utilizatorul face clic pe butonul Trimiteți, va executa acțiunea de trimitere în implementarea componentelor noastre. În cadrul procesorului de acțiuni de trimitere vom obține valoarea mesajului, care este stabilit de introducerea textului. Apoi vom trimite acțiunea împreună cu mesajul. În cele din urmă, vom reseta mesajul la un șir gol.

Celălalt lucru ciudat pe care îl vezi aici este Ember.run.schedule metoda fiind numită. Încă o dată, aceasta este banda de alergare a lui Ember în acțiune. Veți observa că programul ia un șir ca fiind primul argument, în acest caz "dupăRender". Ember are de fapt mai multe cozi diferite pe care le gestionează, făcându-se una dintre ele. Deci, în cazul nostru, spunem când trimiterea mesajului este făcută făcând orice manipulare și după ce coada de randare a fost dezlănțuită, sunați callback-ul nostru. Aceasta ne va derula ul în jos, astfel încât utilizatorul poate vedea noul mesaj după orice manipulare. Pentru mai multe despre bucla de alergare, vă sugerăm să citiți articolul lui Alex Matchneer "Tot ce nu ați vrut să știți despre buclă de alergare".

Dacă mergem la browser și faceți clic pe butonul de expediere, obținem o eroare foarte frumoasă de la Ember, spunând: "Eroare necorespunzătoare: Nimic nu a gestionat evenimentul" sendMessage. "Asta ne așteptăm pentru că nu am spus aplicația noastră despre cum să reacționăm la aceste tipuri de evenimente. Să rezolvăm asta.

 App.IndexRoute = Ember.Route.extend (/ * ... * / acțiuni: sendMessage: funcția (mesajul) if (message! == ") console.log (message););

Acum, dacă revenim la tipul de browser în mesajul de introducere a mesajului și suntem expediate, ar trebui să vedem mesajul din consola. Deci, în acest moment, componenta noastră este cuplată în mod liber și vorbind cu restul aplicației noastre. Să facem ceva mai interesant cu asta. Mai întâi să creăm un nou Ember.Object să funcționeze ca model pentru un nou mesaj.

 App.Message = Ember.Object.extend (id: 3, firstName: 'Chad', lastName: 'Hietala', twitterUserName: 'chadhietala', text: null, timeStamp: null);

Deci, atunci când Trimite mesaj acțiunea apare pe care o vom dori să umplem textul și timestamp-ul câmpul modelului nostru de mesaje, creați o nouă instanță a acestuia și apoi împingeți acea instanță în colecția de mesaje existente.

 App.IndexRoute = Ember.Route.extend (/ * ... * / actions: sendMessage: functie (mesaj) var utilizator, mesaje, newMessage; '), newMessage = App.Message.create (text: message, timeStamp: Date.now ()) messages.pushObject (newMessage););

Când ne întoarcem la browser, ar trebui să putem crea mesaje noi.

Acum avem câteva bucăți diferite de reutilizare ale UI pe care le putem plasa oriunde. De exemplu, dacă aveați nevoie să utilizați un avatar în altă parte a aplicației dvs. Ember, putem reutiliza componenta avatar-utilizator.

 

Împachetarea pluginurilor jQuery

În acest moment, probabil că vă întrebați "Ce se întâmplă dacă vreau să folosesc un plugin jQuery în componenta mea?" Nici o problema. Pentru scurtcircuit, permiteți modificarea componentei de utilizator-avatar pentru a afișa un sfat pentru un instrument atunci când vom trece peste avatarul. Am ales să utilizez tooltipul plugin-ului jQuery pentru a gestiona tooltipul. Să modificăm codul existent pentru a utiliza tooltipster.

Mai întâi, să adăugăm fișiere corecte la adresa noastră chat.html și să modificați componenta avatar existentă.

... ...  ... 

Și apoi JavaScript-ul nostru:

 App.UserAvatarComponent = Ember.Component.extend (/ * ... * / setupTooltip: function () this. $ ('.avatar') .tooltipster (animation: 'fade' ), destroyTooltip: function () this. $ ('.avatar') .tooltipster ('distruge'); .on ('willDestroyElement'));

Încă o dată, vedem sintaxa ascultătorului evenimentului declarativ, dar pentru prima dată vedem acest lucru. $. Dacă sunteți familiarizați cu jQuery, v-ați aștepta să interogăm toate elementele cu clasa de "avatar". Nu este cazul în Ember deoarece contextul este aplicat. În cazul nostru, căutăm numai elemente cu clasa "avatar" din componenta utilizator-avatar. Este comparabil cu metoda de căutare a lui jQuery. La distrugerea elementului, ar trebui să desfacem evenimentul de hover pe avatar și să curățăm orice funcționalitate, acest lucru se face trecând "distruge" pe tipterul de instrument. Dacă mergem la browser, reîmprospătați și treceți o imagine, ar trebui să vedem numele de utilizator al utilizatorului.


Concluzie

În acest tutorial, am făcut o scufundare profundă în componentele Ember și am arătat cum puteți să luați bucăți reutilizabile de UI pentru a genera compozite mai mari și pentru a integra pluginurile jQuery. Ne-am uitat la modul în care componentele sunt diferite de cele din Ember. Am abordat, de asemenea, ideea de programare bazată pe interfață în ceea ce privește componentele. Sper că am reușit să strălucească puțină lumină nu doar asupra componentelor Ember, ci asupra componentelor Web și în care Web-ul este condus.

Cod