Componenti di brace una profonda immersione

Ember.js è un framework MVC JavaScript che consente agli sviluppatori di creare applicazioni web ambiziose. Sebbene il puro MVC consenta a uno sviluppatore di separare le preoccupazioni, non fornisce tutti gli strumenti necessari e l'applicazione richiede altri costrutti. Oggi parlerò di uno di questi costrutti. I componenti Ember sono essenzialmente blocchi riutilizzabili dell'interfaccia utente in sandbox. Se non hai dimestichezza con Ember, consulta la guida introduttiva a Ember.js o Impariamo il percorso di tana. In questo tutorial, tratteremo la specifica dei componenti Web, impareremo come scrivere un componente in Ember, parlare di composizione, spiegare la differenza tra una vista Ember e un componente Ember e fare pratica con l'integrazione di plug-in con componenti Ember.


Una parola sui componenti Web

I componenti Ember si basano sulla specifica dei componenti Web W3C. La specifica è composta da quattro specifiche più piccole; modelli, decoratori, DOM ombra e elementi personalizzati. Di questi quattro concetti solo tre hanno specifiche più rigide, i decoratori sono l'eccezione. Avendo le specifiche in vigore, gli sviluppatori di framework sono stati in grado di eseguire il polyfill di queste nuove API prima che fossero implementate dai fornitori di browser.

Ci sono diversi concetti importanti da comprendere quando si parla di componenti:

  • I componenti non sanno nulla del mondo esterno se non vengono esplicitamente passati
  • I componenti dovrebbero avere un'interfaccia ben definita per il mondo esterno
  • I componenti non possono manipolare alcun JavaScript al di fuori del componente
  • I componenti possono trasmettere eventi
  • Gli elementi personalizzati devono essere assegnati a un nome con un trattino
  • Al di fuori di JavaScript non è possibile manipolare i componenti

I componenti Web forniscono un vero incapsulamento per i widget dell'interfaccia utente. Di seguito è riportato un diagramma di come un componente funziona al livello più elementare.

Mentre Ember ha soddisfatto con successo molte specifiche, i framework come AngularJS, Dart, Polymer e Xtags hanno soluzioni simili. L'unica avvertenza qui è che Ember e Angular attualmente non riguardano gli stili del componente. Nel tempo, queste soluzioni di polyfill svaniranno e le strutture adotteranno l'implementazione del fornitore del browser. Si tratta di un approccio allo sviluppo fondamentalmente diverso, in quanto possiamo trarre vantaggio dalle specifiche future senza vincolarci alle funzionalità sperimentali dei browser.


La componente Ember più semplice

Con la nostra conoscenza dei componenti Web, implementiamo il componente del mio nome molto basilare dall'alto, ma in Ember. Iniziamo scaricando l'Ember Starter Kit dal sito Web di Ember. Al momento di questo tutorial la versione di Ember è 1.3.0. Una volta scaricato, apri i file nel tuo editor preferito, elimina tutti i modelli in index.html (denotato con data-template-name) e tutto in app.js.

La prima cosa che vorremmo fare è creare il nostro modello di componente. Per il bene di questo tutorial useremo modelli in linea. Lo fai scrivendo quanto segue nel tuo index.html file. Abbiamo anche bisogno di creare una nuova applicazione Ember nel nostro JavaScript.

   var app = Ember.Application.create ();

Noterai che il nome del modello di dati ha un nome di percorso invece di una semplice stringa. Il motivo per cui prefisso il nome del componente con "Componenti /" è dire a Ember che abbiamo a che fare con un modello di componente e non con un modello di applicazione normale. Noterai anche che il nome del componente contiene il trattino. Questo è il namespace che ho menzionato nella specifica dei componenti Web. Il namespace è fatto in modo che non abbiamo collisioni di nomi con tag esistenti.

Se apriamo il browser, non dovremmo vedere nulla di diverso. La ragione di ciò è che non dobbiamo ancora inserire nulla nel nostro template del mio nome. Prendiamoci cura di questo.

... 

Ora nel browser dovresti vedere qualcosa come l'immagine sopra. Non abbiamo ancora finito, come puoi vedere, in realtà non stiamo stampando un nome. Come ho detto nella prima sezione, i componenti dovrebbero esporre un'interfaccia ben definita al mondo esterno. In questo caso, siamo interessati al nome. Quindi passiamo al nome posizionando un attributo name sul componente my name.

... 

Quando aggiorni la pagina dovresti vedere "Ciao, mi chiamo Chad". Tutto questo con la scrittura di una riga di JavaScript. Ora che abbiamo un'idea di come scrivere un componente di base, parliamo della differenza tra i componenti di Brace e le viste di Brace.


Componenti di brace e viste di braci

Ember è un MVC, quindi alcuni potrebbero pensare: "Perché non usare solo una vista per questo?" Questa è una domanda legittima. I componenti in realtà sono una sottoclasse di Ember.View, la differenza più grande qui è che le viste si trovano generalmente nel contesto di un controller. Prendi l'esempio qui sotto.

 App.IndexController = Ember.Controller.extend (myState: 'on'); App.IndexView = Ember.View.extend (click: function () var controller = this.get ('controller'), myState = controller.get ('myState'); console.log (controller) // Il controller istanza console.log (myState) // La stringa "on");
 

Le viste normalmente si trovano dietro un modello e trasformano l'input non elaborato (clic, mouseEnter, mouseMove, ecc.) In un'azione semantica (openMenu, editName, hideModal, ecc.) In un controller o percorso. Un'altra cosa da sottolineare è che i modelli hanno bisogno anche di un contesto. Quindi, quello che succede è che Ember deduce il contesto attraverso le convenzioni di denominazione e l'URL. Vedi lo schema qui sotto.

Come puoi vedere, c'è un livello di gerarchia basato sull'URL e ogni livello di quella gerarchia ha il suo contesto derivato dalle convenzioni di denominazione.

I componenti Ember non hanno un contesto, conoscono solo l'interfaccia che definiscono. Ciò consente a un componente di essere reso in qualsiasi contesto, rendendolo disaccoppiato e riutilizzabile. Se il componente espone un'interfaccia, è compito del contesto soddisfare tale interfaccia. In altre parole, se si desidera che il componente esegua correttamente il rendering, è necessario fornirlo con i dati che è in attesa. È importante notare che questi valori passati possono essere sia stringhe che proprietà associate.

Quando le proprietà associate vengono manipolate all'interno di un componente, tali modifiche vengono comunque propagate ovunque vengano referenziate nell'applicazione. Questo rende i componenti estremamente potenti. Ora che abbiamo una buona conoscenza di come i componenti sono diversi dalle viste, diamo un'occhiata a un esempio più complesso che illustra come uno sviluppatore può comporre più componenti.


Composizione dei componenti

Una cosa veramente bella di Ember è che è costruita su concetti di gerarchia dell'interfaccia utente e questo è molto evidente con la composizione dei componenti. Di seguito è riportato un esempio di ciò che faremo. È una semplice interfaccia utente di chat di gruppo. Ovviamente, non ho intenzione di scrivere un intero servizio di chat per alimentare l'interfaccia utente, ma possiamo vedere come possiamo suddividere l'interfaccia utente in componenti riutilizzabili e comporrebili.

Diamo prima un'occhiata a come suddivideremo l'interfaccia utente in parti più piccole e più digeribili. Tutto ciò che possiamo tracciare un parallelepipedo è un componente, ad eccezione degli input di testo e pulsante nella parte inferiore dell'interfaccia utente. Il nostro obiettivo è essere in grado di configurare solo il componente sul livello esterno, tutto il resto dovrebbe funzionare.

Iniziamo creando un nuovo file html chiamato chat.html e impostando tutte le dipendenze per Ember. Quindi crea tutti i modelli.

       

Vedrai che i componenti possono essere annidati all'interno di altri componenti. Questo rende i componenti come i lego che possiamo assemblare come vogliamo. Abbiamo solo bisogno di scrivere nell'interfaccia del componente.

Se ora andiamo a cercare nel browser, non dovremmo vedere molto perché non abbiamo alcun dato che scorre nel componente. Noterai anche che anche se non ci sono dati, i componenti non generano un errore. L'unica cosa che viene effettivamente visualizzata qui è l'area di immissione e il pulsante di invio. Questo perché non dipendono da ciò che è passato.

Guardando più da vicino i modelli noterai che abbiamo assegnato un paio di cose al componente di chat di gruppo.

 

In questo caso, stiamo passando il modello dal contesto del IndexRoute come "messaggi" e abbiamo impostato la stringa di invia messaggio come l'azione sul componente. L'azione verrà utilizzata per trasmettere quando l'utente desidera inviare un nuovo messaggio. Lo illustreremo più avanti nel tutorial. L'altra cosa che noterete è che stiamo impostando interfacce rigorose con i componenti nidificati che utilizzano tutti i dati trasmessi dall'interfaccia di chat di gruppo.

... 
    messaggio #each nei messaggi
  • chat-message username = message.twitterUserName message = message.text time = message.timeStamp
  • /ogni
...

Come accennato in precedenza, è possibile trasferire stringhe o proprietà associate nei componenti. Essendo la regola empirica, usa le virgolette quando passi una stringa, non usare le virgolette quando passi una proprietà legata. Ora che abbiamo i nostri modelli sul posto, gettiamo alcuni dati falsi su di esso.

 App = Ember.Application.create (); App.IndexRoute = Ember.Route.extend (model: function () return [id: 1, firstName: 'Tom', lastName: 'Dale', twitterUserName: 'tomdale', testo: 'Penso che dovremmo tornare vecchio Tomster. È stato fantastico. ', timeStamp: Date.now () - 400000,, id: 2, firstName:' Yehuda ', lastName:' Katz ', twitterUserName:' wycats ', testo:' That '' una buona idea. ', timeStamp: Date.now () - 300000,];);

Se andiamo a vedere questo nel browser ora, dovremmo vedere un po 'di progressi. Ma c'è ancora del lavoro da fare, principalmente far vedere le immagini, formattare la data e poter inviare un nuovo messaggio. Prendiamoci cura di questo.

Con il nostro componente user-avatar, vogliamo utilizzare un servizio chiamato Avatars.io per recuperare l'avatar di Twitter di un utente in base al nome utente di Twitter. Diamo un'occhiata a come viene utilizzato il componente immagine utente nel modello.

  

È un componente piuttosto semplice, ma noterete che abbiamo una proprietà associata chiamata avatarUrl. Avremo bisogno di creare questa proprietà all'interno del nostro JavaScript per questo componente. Un'altra cosa che noterai è che stiamo specificando il servizio da cui vogliamo prelevare l'avatar. Avatars.io ti permette di recuperare avatar sociali da Twitter, Facebook e Instagram. Possiamo rendere questo componente estremamente flessibile. Scriviamo il componente.

 App.UserAvatarComponent = Ember.Component.extend (avatarUrl: function () var username = this.get ('username'), service = this.get ('service'), availableServices = ['twitter', 'facebook' , 'instagram']; if (availableServices.indexOf (service)> -1) return 'http://avatars.io/' + service + '/' + username; return 'images / cat.png'; .property ('username', 'service'));

Come puoi vedere, per creare un nuovo componente seguiamo semplicemente la convenzione di denominazione di NAMEOFCOMPONENTComponent ed estendere Ember.Component. Ora se torniamo al browser, ora dovremmo vedere i nostri avatar.

Per occuparci della formattazione della data, usiamo moment.js e scriviamo un helper di Handlebars per formattare la data per noi.

 Ember.Handlebars.helper ('format-date', function (date) return moment (date) .fromNow (););

Ora tutto ciò che dobbiamo fare è applicare l'helper al nostro componente timestamp.

 

Dovremmo ora avere un componente che formatta le date invece dei timestamp di epoca di Unix.

Possiamo fare uno migliore però. Questi timestamp dovrebbero essere aggiornati automaticamente in modo approssimativo, quindi facciamo in modo che il nostro componente del timestamp faccia proprio questo.

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

Un paio di punti da notare qui. Uno è il sopra() sintassi del gestore di eventi dichiarativo. Questo è stato introdotto in Ember prima della versione 1.0. Fa esattamente quello che pensi che faccia, quando il componente del timestamp è inserito nel DOM, scheduleStartTime è chiamato. Quando l'elemento sta per essere distrutto e ripulito il KillTimer il metodo sarà chiamato. Il resto del componente dice solo il tempo di aggiornare ogni minuto.

L'altra cosa che noterete è che ci sono diverse chiamate a Ember.run. In Ember esiste un sistema di accodamento, normalmente definito come il ciclo di esecuzione, che viene svuotato quando i dati vengono modificati. Questo viene fatto fondamentalmente per unire le modifiche e apportare il cambiamento una volta. Nel nostro esempio che useremo Ember.run.later per eseguire il startTimer metodo ogni minuto Useremo anche Ember.run.cancel per deselezionare il timer. Questo è essenzialmente il metodo di start e stop dell'intervallo di Ember. Sono necessari per mantenere sincronizzato il sistema di accodamento. Per ulteriori informazioni sul ciclo di esecuzione suggerisco di leggere l'articolo di Alex Matchneer "Tutto quello che non hai mai voluto sapere sull'Abrber Run Loop".

La prossima cosa che dobbiamo fare è impostare l'azione in modo che quando l'utente preme Invio, verrà creato un nuovo messaggio. Il nostro componente non dovrebbe preoccuparsi di come vengono creati i dati, dovrebbe solo trasmettere che l'utente ha provato a inviare un messaggio. Nostro IndexRoute sarà responsabile di intraprendere questa azione e trasformarsi in qualcosa di significativo.

 App.GroupChatComponent = Ember.Component.extend (message: ", actions: submit: function () var message = this.get ('message') .trim (), conversation = this. $ ('Ul') [0]; // Rileva il valore di 'action' // e invia l'azione con il messaggio this.sendAction ('action', message); // Quando il ciclo di esecuzione di Ember è terminato // scorri fino alla fine di Ember. run.schedule ('afterRender', function () conversation.scrollTop = conversation.scrollHeight;); // Reimposta il campo del messaggio di testo this.set ('message', "););
 
input type = "text" placeholder = "Invia nuovo messaggio" value = messaggio input type = "submit" value = "Invia"

Poiché il componente chat di gruppo possiede il pulsante di input e di invio, è necessario reagire all'utente facendo clic su invia a questo livello di astrazione. Quando l'utente fa clic sul pulsante di invio, eseguirà l'azione di invio nella nostra implementazione del componente. All'interno del gestore di azioni di invio otterremo il valore del messaggio, che è impostato dall'input del testo. Invieremo quindi l'azione insieme al messaggio. Infine, ripristineremo il messaggio su una stringa vuota.

L'altra cosa strana che vedi qui è la Ember.run.schedule metodo chiamato. Ancora una volta questo è il ciclo di corsa di Ember in azione. Noterai che il programma prende una stringa come primo argomento, in questo caso "afterRender". Ember ha in realtà diverse code che gestisce, rendendole una di queste. Quindi, nel nostro caso, stiamo dicendo che quando l'invio del messaggio è stato effettuato facendo delle manipolazioni e dopo che la coda di rendering è stata scaricata, chiama il nostro callback. Questo scorrerà il nostro ul in basso in modo che l'utente possa vedere il nuovo messaggio dopo ogni manipolazione. Per ulteriori informazioni sul ciclo di esecuzione, ti suggerisco di leggere l'articolo di Alex Matchneer "Tutto quello che non hai mai voluto sapere sull'Ebrber Run Loop".

Se andiamo al browser e clicchiamo sul pulsante invia, riceviamo un errore molto carino da Ember che dice "Errore non rilevato: nulla ha gestito l'evento" sendMessage ". Questo è ciò che ci aspettiamo perché non abbiamo detto alla nostra applicazione come per reagire a questi tipi di eventi. Risolviamolo.

 App.IndexRoute = Ember.Route.extend (/ * ... * / actions: sendMessage: function (message) if (message! == ") console.log (messaggio););

Ora se torniamo al browser digita qualcosa nell'input del messaggio e premi invio, dovremmo vedere il messaggio nella console. Quindi a questo punto il nostro componente è liberamente accoppiato e parla con il resto della nostra applicazione. Facciamo qualcosa di più interessante con questo. Per prima cosa creiamo un nuovo Ember.Object lavorare come modello per un nuovo messaggio.

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

Quindi quando il invia messaggio azione si verifica che vogliamo popolare il testo e timeStamp campo del nostro modello di messaggio, crea una nuova istanza di esso, quindi inserisci tale istanza nella raccolta di messaggi esistente.

 App.IndexRoute = Ember.Route.extend (/ * ... * / actions: sendMessage: function (message) var user, messages, newMessage; if (message! == ") messages = this.modelFor ('index '), newMessage = App.Message.create (text: message, timeStamp: Date.now ()) messages.pushObject (newMessage););

Quando torniamo al browser, ora dovremmo essere in grado di creare nuovi messaggi.

Ora abbiamo diversi blocchi riutilizzabili dell'interfaccia utente che possiamo posizionare ovunque. Ad esempio, se hai bisogno di usare un avatar da qualche altra parte nell'applicazione Ember, possiamo riutilizzare il componente utente-avatar.

 

Avvolgimento di plugin jQuery

A questo punto, probabilmente ti starai chiedendo "E se volessi usare qualche plugin jQuery nel mio componente?" Nessun problema. Per brevità, modifichiamo il nostro componente user-avatar per mostrare un suggerimento quando passiamo il mouse sopra l'avatar. Ho scelto di usare il tooltipster del plugin jQuery per gestire il suggerimento. Modifichiamo il codice esistente per utilizzare tooltipster.

Innanzitutto, aggiungiamo i file corretti al nostro chat.html e modificare il componente avatar utente esistente.

... ...  ... 

E poi il nostro JavaScript:

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

Ancora una volta, vediamo la sintassi del listener di eventi dichiarativi, ma per la prima volta vediamo questo. $. Se hai familiarità con jQuery, ti aspetteresti che dovremmo interrogare tutti gli elementi con la classe di 'avatar'. Questo non è il caso in Ember perché viene applicato il contesto. Nel nostro caso, cerchiamo solo elementi con la classe di 'avatar' nel componente user-avatar. È paragonabile al metodo di ricerca di jQuery. In caso di distruzione dell'elemento, dovremmo eliminare l'evento hover sull'avatar e ripulire qualsiasi funzionalità, questo viene fatto passando 'destroy' a tool tipster. Se andiamo al browser, aggiorna e al passaggio del mouse su un'immagine dovremmo vedere il nome utente dell'utente.


Conclusione

In questo tutorial, abbiamo approfondito i componenti di Ember e mostrato come è possibile utilizzare blocchi di UI riutilizzabili per generare composizioni più grandi e integrare plug-in jQuery. Abbiamo esaminato in che modo i componenti sono diversi dalle viste in Ember. Abbiamo anche parlato dell'idea di programmazione basata sull'interfaccia quando si tratta di componenti. Spero di essere stato in grado di far luce non solo sui componenti di Ember ma sui componenti Web e sul Web.