Glutbestandteile Ein tiefer Tauchgang

Ember.js ist ein JavaScript-MVC-Framework, mit dem Entwickler anspruchsvolle Webanwendungen erstellen können. Pure MVC ermöglicht es zwar einem Entwickler, Bedenken zu trennen, bietet Ihnen jedoch nicht alle Tools, und Ihre Anwendung benötigt andere Konstrukte. Heute werde ich über eines dieser Konstrukte sprechen. Glutbestandteile sind im Wesentlichen wiederverwendbare Teile der Benutzeroberfläche. Wenn Sie mit Ember nicht vertraut sind, lesen Sie den Abschnitt Erste Schritte mit Ember.js oder den Let's Learn Ember-Kurs. In diesem Lernprogramm werden die Web-Komponentenspezifikationen behandelt. Sie erfahren, wie man eine Komponente in Ember schreibt, über die Komposition spricht, den Unterschied zwischen einer Ember-Ansicht und einer Ember-Komponente erklärt und die Integration von Plugins mit Ember-Komponenten üben.


Ein Wort über Webkomponenten

Ember-Komponenten basieren auf der Spezifikation der W3C-Webkomponenten. Die Spezifikation besteht aus vier kleineren Spezifikationen; Vorlagen, Dekorateure, Schatten-DOM und benutzerdefinierte Elemente. Von diesen vier Konzepten haben nur drei Härtespezifikationen, wobei Dekorateure die Ausnahme sind. Durch die Einführung der Spezifikationen konnten Framework-Entwickler diese neuen APIs vor der Implementierung durch Browser-Hersteller auffüllen.

Wenn Sie über Komponenten sprechen, sollten Sie einige wichtige Konzepte verstehen:

  • Komponenten wissen nichts über die Außenwelt, wenn sie nicht explizit übergeben werden
  • Komponenten sollten eine gut definierte Schnittstelle zur Außenwelt haben
  • Komponenten können kein JavaScript außerhalb der Komponente bearbeiten
  • Komponenten können Ereignisse ausstrahlen
  • Benutzerdefinierte Elemente müssen einen Namensraum mit einem Bindestrich enthalten
  • Außerhalb von JavaScript können Komponenten nicht bearbeitet werden

Webkomponenten bieten eine echte Kapselung für UI-Widgets. Nachfolgend ist ein Diagramm dargestellt, wie eine Komponente auf der einfachsten Ebene funktioniert.

Während Ember viele Spezifikationen erfolgreich erfüllt hat, bieten Frameworks wie AngularJS, Dart, Polymer und Xtags ähnliche Lösungen. Der einzige Nachteil hierbei ist, dass Ember und Angular derzeit keinen Stil für die Komponente haben. Im Laufe der Zeit werden diese Polyfill-Lösungen verschwinden, und Frameworks werden die Implementierung des Browseranbieters übernehmen. Dies ist ein grundlegend anderer Entwicklungsansatz, da wir zukünftige Spezifikationen nutzen können, ohne uns an experimentelle Funktionen in Browsern zu binden.


Die grundlegendste Glutkomponente

Mit unserem Wissen über Webkomponenten implementieren wir die sehr einfache my-name-Komponente von oben, jedoch in Ember. Beginnen wir mit dem Herunterladen des Ember Starter Kit von der Ember-Website. Zum Zeitpunkt dieses Tutorials ist die Version von Ember 1.3.0. Wenn Sie die Dateien in Ihrem bevorzugten Editor geöffnet haben, löschen Sie alle Vorlagen in index.html (gekennzeichnet mit data-template-name) und alles in app.js.

Als erstes wollen wir unsere Komponentenvorlage erstellen. In diesem Tutorial werden Inline-Vorlagen verwendet. Sie tun dies, indem Sie folgendes in Ihr schreiben index.html Datei. Wir müssen auch eine neue Ember-Anwendung in unserem JavaScript erstellen.

   var App = Ember.Application.create ();

Sie werden feststellen, dass der Datenvorlagenname einen Pfadnamen anstelle einer einfachen Zeichenfolge hat. Der Grund, warum wir unseren Komponentennamen voranstellen "Komponenten /" Um Ember mitzuteilen, handelt es sich um eine Komponentenvorlage und nicht um eine reguläre Anwendungsvorlage. Sie werden auch feststellen, dass der Komponentenname den Bindestrich enthält. Dies ist der Namespace, den ich in der Web Components-Spezifikation erwähnt hatte. Namespacing erfolgt so, dass keine Namenskollisionen mit vorhandenen Tags auftreten.

Wenn wir den Browser öffnen, sollten wir nichts anderes sehen. Der Grund dafür, dass wir noch nichts in unsere My-Name-Vorlage einfügen müssen. Kümmern wir uns darum.

Jetzt sollten Sie im Browser so etwas wie das Bild oben sehen. Wir sind immer noch nicht fertig, da Sie sehen können, dass wir eigentlich keinen Namen drucken. Wie ich im ersten Abschnitt erwähnt habe, sollten Komponenten der Außenwelt eine gut definierte Schnittstelle bieten. In diesem Fall handelt es sich um den Namen. Lassen Sie uns also den Namen übergeben, indem Sie ein Namensattribut in die my-name-Komponente einfügen.

Wenn Sie die Seite aktualisieren, sollten Sie sehen "Hallo, ich heiße Chad". All dies mit dem Schreiben einer JavaScript-Zeile. Nun, da wir das Gefühl haben, eine grundlegende Komponente zu schreiben, wollen wir über den Unterschied zwischen Ember-Komponenten und Ember-Ansichten sprechen.


Glutbestandteile vs. Glutansichten

Ember ist eine MVC, daher denken einige vielleicht: "Warum verwenden Sie nicht einfach eine Ansicht dafür?" Dies ist eine legitime Frage. Komponenten sind eigentlich eine Unterklasse von Ember.View. Der größte Unterschied besteht darin, dass Ansichten normalerweise im Kontext eines Controllers gefunden werden. Nehmen Sie das Beispiel unten.

 App.IndexController = Ember.Controller.extend (myState: 'on'); App.IndexView = Ember.View.extend (click: function () var controller = this.get ('controller'), meinState = controller.get ('myState'); console.log (controller) // Der Controller Instanz console.log (myState) // Die Zeichenfolge "on");
 

Ansichten sitzen normalerweise hinter einer Vorlage und verwandeln rohe Eingaben (Klicken, mouseEnter, mouseMove usw.) in eine semantische Aktion (openMenu, editName, hideModal usw.) in einem Controller oder einer Route. Eine weitere Sache ist, dass Vorlagen auch einen Kontext benötigen. Am Ende passiert also, dass Ember den Kontext durch Namenskonventionen und die URL einleitet. Siehe nachfolgendes Diagramm.

Wie Sie sehen, gibt es eine Hierarchieebene, die auf der URL basiert, und jede Ebene dieser Hierarchie hat ihren eigenen Kontext, der durch Namenskonventionen abgeleitet wird.

Ember-Komponenten haben keinen Kontext, sie kennen nur die von ihnen definierte Schnittstelle. Dadurch kann eine Komponente in jeden Kontext gerendert werden, sodass sie entkoppelt und wiederverwendbar ist. Wenn für die Komponente eine Schnittstelle verfügbar ist, muss der Kontext diese Schnittstelle erfüllen. Mit anderen Worten, wenn Sie möchten, dass die Komponente ordnungsgemäß gerendert wird, müssen Sie ihr die erwarteten Daten zur Verfügung stellen. Es ist wichtig zu wissen, dass diese übergebenen Werte sowohl Zeichenfolgen als auch gebundene Eigenschaften sein können.

Wenn gebundene Eigenschaften innerhalb einer Komponente bearbeitet werden, werden diese Änderungen immer dort weitergegeben, wo sie in Ihrer Anwendung referenziert werden. Dies macht Komponenten extrem leistungsstark. Nun, da wir ein gutes Verständnis dafür haben, wie sich Komponenten von Ansichten unterscheiden, betrachten wir ein komplexeres Beispiel, das veranschaulicht, wie ein Entwickler mehrere Komponenten zusammensetzen kann.


Zusammensetzung der Komponenten

Eine sehr schöne Sache bei Ember ist, dass es auf Konzepten der UI-Hierarchie aufbaut und dies ist bei der Zusammensetzung der Komponenten sehr offensichtlich. Unten sehen Sie ein Beispiel dafür, was wir machen werden. Es ist eine einfache Benutzeroberfläche für Gruppenchat. Natürlich schreibe ich nicht einen ganzen Chat-Dienst, um die Benutzeroberfläche zu betreiben, aber wir können sehen, wie wir die Benutzeroberfläche in wiederverwendbare und komponierbare Komponenten zerlegen können.

Lassen Sie uns zunächst einen Blick darauf werfen, wie wir die Benutzeroberfläche in kleinere und besser verdauliche Teile aufteilen. Alles, was wir um eine Box zeichnen können, ist eine Komponente, mit Ausnahme der Text- und Schaltflächeneingaben am unteren Rand der Benutzeroberfläche. Unser Ziel ist es, die Komponente nur auf der äußeren Ebene konfigurieren zu können. Alles andere sollte funktionieren.

Beginnen wir mit dem Erstellen einer neuen HTML-Datei namens chat.html und alle Abhängigkeiten für Ember einrichten. Als nächstes erstellen Sie alle Vorlagen.

       

Sie werden sehen, dass Komponenten in andere Komponenten geschachtelt werden können. Dies macht Komponenten wie Legos, die wir beliebig zusammenbauen können. Wir müssen nur auf die Schnittstelle der Komponente schreiben.

Wenn wir jetzt im Browser nachsehen, sollten wir nicht viel sehen, da keine Daten in die Komponente fließen. Sie werden auch feststellen, dass die Komponenten keinen Fehler auslösen, obwohl keine Daten vorhanden sind. Das einzige, was hier tatsächlich gerendert wird, ist der Eingabebereich und die Schaltfläche "Senden". Dies liegt daran, dass sie nicht abhängig von dem sind, was übergeben wird.

Wenn Sie sich die Vorlagen etwas genauer ansehen, werden Sie feststellen, dass wir einige Elemente in der Gruppenchat-Komponente zugewiesen haben.

 

In diesem Fall übergeben wir das Modell aus dem Kontext von IndexRoute als "messages" und wir haben den String von gesetzt Nachricht senden als Aktion auf die Komponente. Die Aktion wird verwendet, um zu senden, wenn der Benutzer eine neue Nachricht senden möchte. Wir werden dies später im Tutorial behandeln. Die andere Sache, die Sie bemerken werden, ist, dass wir strikte Schnittstellen zu den verschachtelten Komponenten einrichten, von denen alle die von der Gruppe-Chat-Schnittstelle übergebenen Daten verwenden.

    # Jede Nachricht in Nachrichten
  • chat-message Benutzername = message.twitterUserName message = message.text time = message.timeStamp
  • /jeder

Wie bereits erwähnt, können Sie Strings oder gebundene Eigenschaften an Komponenten übergeben. Als Faustregel gilt: Verwenden Sie Anführungszeichen, wenn Sie eine Zeichenfolge übergeben. Verwenden Sie keine Anführungszeichen, wenn Sie eine gebundene Eigenschaft übergeben. Nachdem wir nun unsere Vorlagen eingerichtet haben, werfen wir ein paar Scheindaten darauf.

 App = Ember.Application.create (); App.IndexRoute = Ember.Route.extend (model: function () return [id: 1, Vorname: 'Tom', Nachname: 'Dale', TwitterBenutzername: 'Tomdale', Text: 'Ich denke, wir sollten zurückkehren alter Tomster. Er war großartig. ', Zeitstempel: Date.now () - 400000, id: 2, Vorname:' Yehuda ', Nachname:' Katz ', twitterUserName:' wycats ', Text:' That \ ' eine gute Idee. ', timeStamp: Date.now () - 300000,];);

Wenn wir uns das jetzt im Browser ansehen, sollten wir ein bisschen Fortschritt sehen. Es gibt jedoch noch einiges zu tun, vor allem, um die Bilder anzuzeigen, das Datum zu formatieren und eine neue Nachricht senden zu können. Kümmern wir uns darum.

Mit unserer Benutzer-Avatar-Komponente möchten wir einen Dienst namens Avatars.io verwenden, um den Twitter-Avatar eines Benutzers anhand seines Twitter-Benutzernamens abzurufen. Sehen wir uns an, wie die Benutzerbildkomponente in der Vorlage verwendet wird.

  

Es ist eine ziemlich einfache Komponente, aber Sie werden feststellen, dass wir eine gebundene Eigenschaft haben avatarUrl. Wir müssen diese Eigenschaft in unserem JavaScript für diese Komponente erstellen. Eine weitere Sache, die Sie beachten werden, ist, dass wir den Dienst angeben, von dem der Avatar abgerufen werden soll. Mit Avatars.io können Sie soziale Avatare von Twitter, Facebook und Instagram abrufen. Wir können diese Komponente extrem flexibel gestalten. Lassen Sie uns die Komponente schreiben.

 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 + '/' + Benutzername; return 'images / cat.png'; .property ('Benutzername', 'Service'));

Um eine neue Komponente zu erstellen, folgen Sie einfach der Namenskonvention von NAMEOFCOMPONENTComponent und erweitern Glut.Komponente. Wenn wir jetzt zum Browser zurückkehren, sollten wir jetzt unsere Avatare sehen.

Um sich um die Datumsformatierung zu kümmern, verwenden wir moment.js und schreiben einen Handlebars-Helfer, um das Datum für uns zu formatieren.

 Ember.Handlebars.helper ('format-date', Funktion (Datum) Rückkehrzeitpunkt (Datum) .fromNow (););

Jetzt müssen wir nur noch den Helfer auf unsere Zeitstempelkomponente anwenden.

 

Wir sollten jetzt eine Komponente haben, die Datumsangaben anstelle der Zeitstempel der Unix-Epoche formatiert.

Wir können es aber besser machen. Diese Zeitstempel sollten sich im Lauf der Zeit automatisch aktualisieren. Lassen Sie uns also unsere Zeitstempelkomponente genau so machen.

 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'));

Ein paar Punkte, die hier zu beachten sind. Eins ist das auf() Deklarative Eventhandler-Syntax. Dieses wurde in Ember vor der Version 1.0 eingeführt. Es tut genau das, was Sie denken, wenn die Zeitstempelkomponente in das DOM eingefügt wird, scheduleStartTime wird genannt. Wenn das Element zerstört und bereinigt werden soll killTimer Methode wird aufgerufen. Der Rest der Komponente gibt nur den Zeitpunkt an, um jede Minute zu aktualisieren.

Die andere Sache, die Sie feststellen werden, ist, dass es mehrere Anrufe gibt Ember.run. In Ember gibt es ein Warteschlangensystem, das normalerweise als Laufschleife bezeichnet wird und bei Änderungen der Daten gelöscht wird. Dies geschieht, um Änderungen im Wesentlichen zusammenzuführen und die Änderungen einmal vorzunehmen. In unserem Beispiel werden wir verwenden Ember.run.later die laufen lassen startTimer Methode jede Minute. Wir werden auch verwenden Ember.run.cancel den Timer zu entreißen. Hierbei handelt es sich im Wesentlichen um Embers eigene Start- und Stop-Intervall-Methoden. Sie werden benötigt, um das Warteschlangensystem synchron zu halten. Um mehr über die Laufschleife zu erfahren, schlage ich vor, den Artikel von Alex Matchneer zu lesen "Alles, was Sie noch nie über die Glut-Laufschleife wissen wollten".

Als Nächstes müssen Sie die Aktion so einrichten, dass beim Senden des Benutzers eine neue Nachricht erstellt wird. Unsere Komponente sollte sich nicht darum kümmern, wie die Daten erstellt werden. Es sollte nur gesendet werden, dass der Benutzer versucht hat, eine Nachricht zu senden. Unsere IndexRoute wird dafür verantwortlich sein, diese Aktion zu ergreifen und in etwas Sinnvolles zu verwandeln.

 App.GroupChatComponent = Ember.Component.extend (message: ", actions: submit: function () var message = this.get ('message') .trim (), Konversation = this. $ ('Ul') [0]; // Ruft den Wert von 'action' // ab und sendet die Aktion mit der Nachricht this.sendAction ('action', message); // Wenn die Ember-Laufschleife abgeschlossen ist // zum unteren Ember scrollen. run.schedule ('afterRender', function () conversation.scrollTop = conversation.scrollHeight;); // Das Textfeld zurücksetzen this.set ('message', "););
 
input type = "text" placeholder = "Neue Nachricht senden" value = message input type = "submit" value = "Send"

Da die Gruppen-Chat-Komponente die Eingabe- und Senden-Schaltfläche besitzt, müssen wir auf den Benutzer reagieren, der auf dieser Abstraktionsebene auf Senden klickt. Wenn der Benutzer auf die Schaltfläche "Abschicken" klickt, führt er die Aktion "Abschicken" in unserer Komponentenimplementierung aus. Im Handler zum Senden von Aktionen erhalten wir den Wert von message, der durch die Texteingabe festgelegt wird. Wir senden dann die Aktion zusammen mit der Nachricht. Zum Schluss setzen wir die Nachricht auf eine leere Zeichenfolge zurück.

Die andere seltsame Sache, die Sie hier sehen, ist die Ember.run.schedule Methode aufgerufen wird. Wieder ist dies Embers Laufschleife in Aktion. Sie werden feststellen, dass der Zeitplan eine Zeichenfolge als erstes Argument verwendet, in diesem Fall "afterRender". Ember hat tatsächlich mehrere verschiedene Warteschlangen, die er verwaltet, wobei Render eine von ihnen ist. In unserem Fall sagen wir also, wenn das Senden der Nachricht abgeschlossen ist, und nachdem die Render-Warteschlange geleert wurde, rufen Sie unseren Rückruf an. Dies wird unsere scrollen ul nach unten, damit der Benutzer die neue Nachricht nach allen Manipulationen sehen kann. Um mehr über die Laufschleife zu erfahren, schlage ich vor, den Artikel von Alex Matchneer zu lesen "Alles, was Sie noch nie über die Glut-Laufschleife wissen wollten".

Wenn wir zum Browser gehen und auf die Schaltfläche "Senden" klicken, erhalten wir eine sehr nette Fehlermeldung von Ember: "Uncatch Error: Nichts hat das Ereignis 'sendMessage' behandelt. Dies ist, was wir erwarten, weil wir unserer Anwendung nicht gesagt haben, wie auf diese Art von Ereignissen zu reagieren.

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

Wenn wir jetzt zum Browser zurückgehen, etwas in die Nachrichteneingabe eingeben und auf "Senden" klicken, sollte die Nachricht in der Konsole angezeigt werden. An diesem Punkt ist unsere Komponente also lose gekoppelt und spricht mit dem Rest unserer Anwendung. Lassen Sie uns etwas interessanter machen. Zuerst erstellen wir ein neues Ember.Object als Modell für eine neue Nachricht arbeiten.

 App.Message = Ember.Object.extend (id: 3, Vorname: 'Tschad', Nachname: 'Hietala', twitterUserName: 'chadhietala', Text: null, timeStamp: null);

Also wenn der Nachricht senden Aktion geschieht, wir wollen den Text und füllen Zeitstempel Feld unseres Nachrichtenmodells erstellen, erstellen Sie eine neue Instanz davon, und verschieben Sie diese Instanz in die vorhandene Sammlung von Nachrichten.

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

Wenn wir zum Browser zurückkehren, sollten wir jetzt in der Lage sein, neue Nachrichten zu erstellen.

Wir haben jetzt mehrere verschiedene wiederverwendbare Teile der Benutzeroberfläche, die wir überall platzieren können. Wenn Sie beispielsweise einen Avatar an einer anderen Stelle in Ihrer Ember-Anwendung verwenden mussten, können wir die Benutzer-Avatar-Komponente wiederverwenden.

 

JQuery-Plugins einpacken

An diesem Punkt fragen Sie sich wahrscheinlich "Was ist, wenn ich in meiner Komponente ein jQuery-Plugin verwenden möchte?" Kein Problem. Der Kürze halber wollen wir unsere Benutzer-Avatar-Komponente so ändern, dass ein Tool-Tipp angezeigt wird, wenn Sie den Mauszeiger über den Avatar bewegen. Ich habe mich entschieden, den jQuery-Plugin-Tooltipster zu verwenden, um den Tooltip zu verarbeiten. Lassen Sie uns den vorhandenen Code ändern, um den Tooltipster zu verwenden.

Zuerst fügen wir korrekte Dateien hinzu chat.html und ändern Sie die vorhandene Benutzer-Avatar-Komponente.

 

Und dann unser JavaScript:

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

Wieder sehen wir die deklarative Ereignis-Listener-Syntax, aber zum ersten Mal das. $. Wenn Sie mit jQuery vertraut sind, würden Sie erwarten, dass wir alle Elemente mit der Klasse "avatar" abfragen würden. Dies ist in Ember nicht der Fall, da der Kontext angewendet wird. In unserem Fall suchen wir nur Elemente mit der Klasse "Avatar" in der Benutzer-Avatar-Komponente. Es ist vergleichbar mit der Suchmethode von jQuery. Bei der Zerstörung des Elements sollten Sie das Hover-Ereignis des Avatars aufheben und die Funktionalität bereinigen. Dies geschieht, indem Sie "destroy" an den Tool-Tipster übergeben. Wenn wir zum Browser gehen, ein Bild aktualisieren und bewegen, sollten wir den Benutzernamen des Benutzers sehen.


Fazit

In diesem Tutorial haben wir uns eingehend mit Ember-Komponenten beschäftigt und gezeigt, wie Sie wiederverwendbare Teile der Benutzeroberfläche verwenden können, um größere Composites zu erstellen und jQuery-Plugins zu integrieren. Wir haben untersucht, wie sich Komponenten von Ansichten in Ember unterscheiden. Die Idee der Schnittstellen-basierten Programmierung wurde auch bei Komponenten behandelt. Hoffentlich konnte ich nicht nur die Ember-Komponenten, sondern auch die Web-Komponenten und die Bereiche, in denen sich das Web befindet, ein wenig näher bringen.