Ember Components: Ein tiefer Tauchgang
German (Deutsch) translation by Federicco Ancie (you can also view the original English article)
Ember.js ist ein JavaScript-MVC-Framework, mit dem Entwickler ehrgeizige Webanwendungen erstellen können. Mit reinem MVC kann ein Entwickler die Probleme trennen, bietet Ihnen jedoch nicht alle Werkzeugs, und Ihre Anwendung benötigt andere Konstrukte. Heute werde ich über eines dieser Konstrukte sprechen. Ember-Komponenten sind wiederverwendbare Sandbox-Teile der Benutzeroberfläche. Wenn Sie mit Ember nicht vertraut sind, lesen Sie bitte Erste Schritte mit Ember.js oder den Let's Learn Ember-Kurs. In diesem Tutorial werden wir die Webkomponentenspezifikation behandeln, lernen, 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 in Ember-Komponenten üben.
Ein Wort zu den Webkomponenten
Ember-Komponenten basieren auf der W3C-Webkomponentenspezifikation. Die Spezifikation besteht aus vier kleineren Spezifikationen; Vorlagen, Dekorateure, Schatten-DOM und benutzerdefinierte Elemente. Von diesen vier Konzepten haben nur drei Härtungsspezifikationen, wobei Dekorateure die Ausnahme bilden. Durch die Einführung der Spezifikationen konnten Framework-Entwickler diese neuen APIs mehrfach ausfüllen, bevor sie von Browser-Anbietern implementiert wurden.
Beim Sprechen über Komponenten sind mehrere wichtige Konzepte zu verstehen:
- Komponenten wissen nichts über die Außenwelt, es sei denn, sie werden ausdrücklich weitergegeben
- Komponenten sollten eine gut definierte Schnittstelle zur Außenwelt haben
- Komponenten können kein JavaScript außerhalb der Komponente bearbeiten
- Komponenten können Ereignisse übertragen
- Benutzerdefinierte Elemente müssen mit einem Bindestrich versehen werden
- Außerhalb von JavaScript können Komponenten nicht bearbeitet werden
Webkomponenten bieten eine echte Kapselung für UI-Widgets. Unten sehen Sie ein Diagramm, wie eine Komponente auf der grundlegendsten Ebene funktioniert.



Während Ember viele Spezifikationen erfolgreich polygefüllt hat, haben Frameworks wie AngularJS, Dart, Polymer und Xtags ähnliche Lösungen. Die einzige Einschränkung besteht darin, dass Ember und Angular derzeit keine Stile für die Komponente festlegen. Mit der Zeit werden diese Polyfill-Lösungen verblassen und Frameworks werden die Implementierung des Browser-Anbieters ü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 Enber-Komponente
Lassen Sie uns mit unserem Wissen über Webkomponenten die sehr grundlegende my-name-Komponente von oben implementieren, jedoch in Ember. Beginnen wir mit dem Herunterladen des Ember Starter Kits von der Ember-Website. Zum Zeitpunkt dieses Tutorials ist die Version von Ember 1.3.0. Sobald Sie es heruntergeladen haben, öffnen Sie die Dateien in Ihrem bevorzugten Editor, löschen Sie alle Vorlagen in index.html (gekennzeichnet mit dem Namen der Datenvorlage) und alles in app.js.
Zuerst möchten wir unsere Komponentenvorlage erstellen. Für dieses Tutorial werden wir Inline-Vorlagen verwenden. Sie tun dies, indem Sie Folgendes in Ihre index.html-Datei schreiben. Wir müssen auch eine neue Ember-Anwendung in unserem JavaScript erstellen.
1 |
|
2 |
<script type="text/x-handlebars"> |
3 |
{{my-name}} |
4 |
</script> |
5 |
|
6 |
<script type="text/x-handlebars" data-template-name="components/my-name"> |
7 |
// My component template will go here
|
8 |
</script> |
9 |
|
10 |
var App = Ember.Application.create(); |
Sie werden feststellen, dass der Datenvorlagenname einen Pfadnamen anstelle einer einfachen Zeichenfolge hat. Der Grund, warum wir unserem Komponentennamen "components/" voranstellen, besteht darin, Ember mitzuteilen, dass es sich um eine Komponentenvorlage und nicht um eine reguläre Anwendungsvorlage handelt. Sie werden auch feststellen, dass der Komponentenname den Bindestrich enthält. Dies ist der Namespace, den ich in der Web Components-Spezifikation erwähnt habe. Der Namensraum wird so erstellt, dass keine Namenskollisionen mit vorhandenen Tags auftreten.
Wenn wir den Browser öffnen, sollten wir nichts anderes sehen. Der Grund dafür ist, dass wir noch nichts in unsere My-Name-Vorlage eingefügt haben. Kümmern wir uns darum.
1 |
|
2 |
... |
3 |
<script type="text/x-handlebars" data-template-name="components/my-name"> |
4 |
Hi, my name is {{name}}. |
5 |
</script>
|



Jetzt sollten Sie im Browser so etwas wie das Bild oben sehen. Wir sind immer noch nicht fertig, wie Sie sehen, drucken wir tatsächlich keinen Namen aus. Wie ich im ersten Abschnitt erwähnt habe, sollten Komponenten eine gut definierte Schnittstelle zur Außenwelt bieten. In diesem Fall handelt es sich um den Namen. Übergeben wir also den Namen, indem wir ein Namensattribut auf die my-name-Komponente setzen.
1 |
... |
2 |
<script type="text/x-handlebars"> |
3 |
{{my-name name="Chad"}} |
4 |
</script>
|
Wenn Sie die Seite aktualisieren, sollte "Hallo, mein Name ist Chad" angezeigt werden. All dies mit dem Schreiben einer Zeile JavaScript. Nachdem wir nun ein Gefühl für das Schreiben einer Basiskomponente haben, wollen wir über den Unterschied zwischen Ember-Komponenten und Ember-Ansichten sprechen.
Ember-Komponenten vs. Ember-Ansichten
Ember ist eine MVC, daher denken einige vielleicht: "Warum nicht einfach eine Ansicht dafür verwenden?" Dies ist eine berechtigte Frage. Komponenten sind eigentlich eine Unterklasse von Ember.View. Der größte Unterschied besteht darin, dass Ansichten im Allgemeinen im Kontext eines Controllers gefunden werden. Nehmen Sie das folgende Beispiel.
1 |
|
2 |
App.IndexController = Ember.Controller.extend({ |
3 |
myState: 'on' |
4 |
});
|
5 |
|
6 |
App.IndexView = Ember.View.extend({ |
7 |
|
8 |
click: function () { |
9 |
var controller = this.get( 'controller' ), |
10 |
myState = controller.get( 'myState' ); |
11 |
|
12 |
console.log( controller ) // The controller instance |
13 |
console.log( myState ) // The string "on" |
14 |
}
|
15 |
|
16 |
});
|
1 |
<script type="text/x-handlebars" data-template-name="index"> |
2 |
{{myState}} |
3 |
</script>
|
Ansichten befinden sich normalerweise hinter einer Vorlage und wandeln rohe Eingaben (click, mouseEnter, mouseMove usw.) in eine semantische Aktion (openMenu, editName, hideModal usw.) in einem Controller oder einer Route um. Ein weiterer Punkt ist, dass Vorlagen ebenfalls einen Kontext benötigen. Was am Ende passiert, ist, dass Ember den Kontext durch Namenskonventionen und die URL ableitet. Siehe das Diagramm unten.



Wie Sie sehen können, 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. Auf diese Weise kann eine Komponente in einen beliebigen Kontext gerendert und entkoppelt und wiederverwendet werden. Wenn die Komponente eine Schnittstelle verfügbar macht, ist es Aufgabe des Kontexts, diese Schnittstelle zu erfüllen. Mit anderen Worten, wenn Sie möchten, dass die Komponente ordnungsgemäß gerendert wird, müssen Sie sie mit den erwarteten Daten versorgen. Es ist wichtig zu beachten, dass diese übergebenen Werte sowohl Zeichenfolgen als auch gebundene Eigenschaften sein können.



Wenn gebundene Eigenschaften innerhalb einer Komponente bearbeitet werden, werden diese Änderungen weiterhin dort weitergegeben, wo in Ihrer Anwendung auf sie verwiesen wird. Dies macht Komponenten extrem leistungsfähig. Nachdem wir nun ein gutes Verständnis dafür haben, wie sich Komponenten von Ansichten unterscheiden, schauen wir uns ein komplexeres Beispiel an, das zeigt, wie ein Entwickler mehrere Komponenten zusammensetzen kann.
Zusammensetzung der Komponenten
Eine wirklich schöne Sache an Ember ist, dass es auf Konzepten der UI-Hierarchie basiert und dies bei der Zusammensetzung der Komponenten sehr deutlich wird. Unten sehen Sie ein Beispiel dafür, was wir machen werden. Es ist eine einfache Benutzeroberfläche für Gruppenchat. Natürlich werde ich keinen ganzen Chat-Dienst schreiben, um die Benutzeroberfläche mit Strom zu versorgen, aber wir können sehen, wie wir die Benutzeroberfläche in wiederverwendbare und komponierbare Komponenten aufteilen können.



Schauen wir uns zunächst an, wie wir die Benutzeroberfläche in kleinere und besser verdauliche Teile aufteilen werden. Alles, um das wir ein Kästchen 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 einfach funktionieren.



Beginnen wir mit der Erstellung einer neuen HTML-Datei mit dem Namen chat.html und dem Einrichten aller Abhängigkeiten für Ember. Als nächstes erstellen Sie alle Vorlagen.
1 |
<script type="text/x-handlebars" data-template-name="application"> |
2 |
{{outlet}} |
3 |
</script> |
4 |
|
5 |
<script type="text/x-handlebars" data-template-name="index"> |
6 |
{{ group-chat messages=model action="sendMessage" }} |
7 |
</script> |
8 |
|
9 |
<script type="text/x-handlebars" data-template-name="components/group-chat"> |
10 |
<div class="chat-component"> |
11 |
<ul class="conversation"> |
12 |
{{#each message in messages}} |
13 |
<li class="txt">{{chat-message username=message.twitterUserName message=message.text time=message.timeStamp }}</li> |
14 |
{{/each}} |
15 |
</ul> |
16 |
|
17 |
<form class="new-message" {{action submit on="submit"}}> |
18 |
{{input type="text" placeholder="Send new message" value=message class="txt-field"}} |
19 |
{{input type="submit" class="send-btn" value="Send"}} |
20 |
</form> |
21 |
</div> |
22 |
</script> |
23 |
|
24 |
<script type="text/x-handlebars" data-template-name="components/chat-message"> |
25 |
<div class="message media"> |
26 |
<div class="img"> |
27 |
{{user-avatar username=username service="twitter"}} |
28 |
</div> |
29 |
<div class="bd"> |
30 |
{{user-message message=message}} |
31 |
{{time-stamp time=time}} |
32 |
</div> |
33 |
</div> |
34 |
</script> |
35 |
|
36 |
<script type="text/x-handlebars" data-template-name="components/user-avatar"> |
37 |
<img {{bind-attr src=avatarUrl alt=username}} class="avatar"> |
38 |
</script> |
39 |
|
40 |
<script type="text/x-handlebars" data-template-name="components/user-message"> |
41 |
<div class="user-message">{{message}}</div> |
42 |
</script> |
43 |
|
44 |
<script type="text/x-handlebars" data-template-name="components/time-stamp"> |
45 |
<div class="time-stamp"> |
46 |
<span class="clock" role="presentation"></span> |
47 |
<span class="time">{{format-date time}}</span> |
48 |
</div> |
49 |
</script> |
Sie werden sehen, dass Komponenten in anderen Komponenten verschachtelt werden können. Dies macht Komponenten wie Legos, die wir nach Belieben zusammenbauen können. Wir müssen nur auf die Schnittstelle der Komponente schreiben.
Wenn wir jetzt in den Browser schauen, 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 Senden-Schaltfläche. Dies liegt daran, dass sie nicht davon abhängen, was übergeben wird.



Wenn Sie sich die Vorlagen etwas genauer ansehen, werden Sie feststellen, dass wir der Gruppen-Chat-Komponente einige Dinge zugewiesen haben.
1 |
<script type="text/x-handlebars" data-template-name="index"> |
2 |
{{ group-chat messages=model action="sendMessage" }} |
3 |
</script>
|
In diesem Fall übergeben wir das Modell aus dem Kontext der IndexRoute als "Nachrichten" und haben die Zeichenfolge sendMessage als Aktion für die Komponente festgelegt. Die Aktion wird zum Senden verwendet, wenn der Benutzer eine neue Nachricht senden möchte. Wir werden dies später im Tutorial behandeln. Das andere, was Sie bemerken werden, ist, dass wir strenge Schnittstellen zu den verschachtelten Komponenten einrichten, die alle die von der Gruppen-Chat-Schnittstelle übergebenen Daten verwenden.
1 |
... |
2 |
<ul class="conversation"> |
3 |
{{#each message in messages}}
|
4 |
<li class="txt">{{chat-message username=message.twitterUserName message=message.text time=message.timeStamp }}</li> |
5 |
{{/each}}
|
6 |
</ul>
|
7 |
... |
Wie bereits erwähnt, können Sie Zeichenfolgen oder gebundene Eigenschaften an Komponenten übergeben. Als Faustregel gilt: Verwenden Sie beim Übergeben einer Zeichenfolge Anführungszeichen und beim Übergeben einer gebundenen Eigenschaft keine Anführungszeichen. Nachdem wir unsere Vorlagen eingerichtet haben, werfen wir einige Scheindaten darauf.
1 |
App = Ember.Application.create(); |
2 |
|
3 |
App.IndexRoute = Ember.Route.extend({ |
4 |
model: function() { |
5 |
return [ |
6 |
{
|
7 |
id: 1, |
8 |
firstName: 'Tom', |
9 |
lastName: 'Dale', |
10 |
twitterUserName: 'tomdale', |
11 |
text: 'I think we should back old Tomster. He was awesome.', |
12 |
timeStamp: Date.now() - 400000, |
13 |
},
|
14 |
{
|
15 |
id: 2, |
16 |
firstName: 'Yehuda', |
17 |
lastName: 'Katz', |
18 |
twitterUserName: 'wycats', |
19 |
text: 'That\'s a good idea.', |
20 |
timeStamp: Date.now() - 300000, |
21 |
}
|
22 |
];
|
23 |
}
|
24 |
});
|
Wenn wir uns das jetzt im Browser ansehen, sollten wir ein bisschen Fortschritt sehen. Es gibt jedoch noch einige Arbeiten zu erledigen, hauptsächlich das Anzeigen der Bilder, das Formatieren des Datums und das Senden einer neuen Nachricht. 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 basierend auf seinem Twitter-Benutzernamen abzurufen. Schauen wir uns an, wie die Benutzerbildkomponente in der Vorlage verwendet wird.
1 |
<script type="text/x-handlebars" data-template-name="components/chat-message"> |
2 |
...
|
3 |
{{ user-avatar username=username service="twitter" }} |
4 |
...
|
5 |
</script>
|
6 |
|
7 |
<script type="text/x-handlebars" data-template-name="components/user-avatar"> |
8 |
<img {{bind-attr src=avatarUrl alt=username}} class="avatar"> |
9 |
</script>
|
Es ist eine ziemlich einfache Komponente, aber Sie werden feststellen, dass wir eine gebundene Eigenschaft namens avatarUrl haben. Wir müssen diese Eigenschaft in unserem JavaScript für diese Komponente erstellen. Sie werden auch feststellen, dass wir den Dienst angeben, von dem wir den Avatar abrufen möchten. Mit Avatars.io können Sie soziale Avatare von Twitter, Facebook und Instagram abrufen. Wir können diese Komponente extrem flexibel machen. Schreiben wir die Komponente.
1 |
App.UserAvatarComponent = Ember.Component.extend({ |
2 |
avatarUrl: function () { |
3 |
var username = this.get( 'username' ), |
4 |
service = this.get( 'service' ), |
5 |
availableServices = [ 'twitter', 'facebook', 'instagram' ]; |
6 |
|
7 |
if ( availableServices.indexOf( service ) > -1 ) { |
8 |
return 'http://avatars.io/' + service + '/' + username; |
9 |
}
|
10 |
return 'images/cat.png'; |
11 |
|
12 |
}.property( 'username' , 'service' ) |
13 |
|
14 |
});
|
Wie Sie sehen können, folgen wir zum Erstellen einer neuen Komponente einfach der Namenskonvention von NAMEOFCOMPONENTComponent und erweitern Ember.Component. Wenn wir jetzt zum Browser zurückkehren, sollten wir jetzt unsere Avatare sehen.



Um die Datumsformatierung zu gewährleisten, verwenden wir moment.js und schreiben einen Lenker-Helfer, um das Datum für uns zu formatieren.
1 |
Ember.Handlebars.helper('format-date', function( date ) { |
2 |
return moment( date ).fromNow(); |
3 |
});
|
Jetzt müssen wir nur noch den Helfer auf unsere Zeitstempelkomponente anwenden.
1 |
<script type="text/x-handlebars" data-template-name="components/time-stamp"> |
2 |
<div class="time-stamp"> |
3 |
<span class="clock" role="presentation"></span> |
4 |
<span class="time">{{format-date time}}</span> |
5 |
</div> |
6 |
</script>
|
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 im Laufe der Zeit automatisch aktualisiert werden. Lassen Sie uns also unsere Zeitstempelkomponente genau das tun.
1 |
App.TimeStampComponent = Ember.Component.extend({ |
2 |
|
3 |
startTimer: function () { |
4 |
var currentTime = this.get('time'); |
5 |
this.set('time', currentTime - 6000 ); |
6 |
this.scheduleStartTimer(); |
7 |
},
|
8 |
|
9 |
scheduleStartTimer: function(){ |
10 |
this._timer = Ember.run.later(this, 'startTimer', 6000); |
11 |
}.on('didInsertElement'), |
12 |
|
13 |
killTimer: function () { |
14 |
Ember.run.cancel( this._timer ); |
15 |
}.on( 'willDestroyElement' ) |
16 |
|
17 |
});
|
Ein paar Punkte, die hier zu beachten sind. Eine davon ist die deklarative Ereignishandlersyntax on(). Dies wurde in Ember vor der Version 1.0 eingeführt. Es macht genau das, was Sie denken, wenn die Zeitstempelkomponente in das DOM eingefügt wird, wird scheduleStartTime aufgerufen. Wenn das Element zerstört und bereinigt werden soll, wird die killTimer-Methode aufgerufen. Der Rest der Komponente gibt nur die Zeit an, zu der jede Minute aktualisiert werden muss.
Die andere Sache, die Sie bemerken werden, ist, dass es mehrere Aufrufe von Ember.run gibt. In Ember gibt es ein Warteschlangensystem, das normalerweise als Run-Schleife bezeichnet wird und beim Ändern von Daten gelöscht wird. Dies geschieht, um Änderungen grundsätzlich zusammenzuführen und einmal vorzunehmen. In unserem Beispiel verwenden wir Ember.run.later, um die startTimer-Methode jede Minute auszuführen. Wir werden auch Ember.run.cancel verwenden, um den Timer aus der Warteschlange zu entfernen. Dies ist im Wesentlichen Embers eigene Start- und Stoppintervallmethoden. Sie werden benötigt, um das Warteschlangensystem synchron zu halten. Um mehr über die Run-Schleife zu erfahren, empfehle ich, Alex Matchneers Artikel "Alles, was Sie nie über die Ember-Run-Schleife wissen wollten" zu lesen.
Als nächstes müssen wir die Aktion so einrichten, dass eine neue Nachricht erstellt wird, wenn der Benutzer auf "Senden" klickt. Unsere Komponente sollte sich nicht darum kümmern, wie die Daten erstellt werden. Sie sollte lediglich senden, dass der Benutzer versucht hat, eine Nachricht zu senden. Unsere IndexRoute wird dafür verantwortlich sein, diese Maßnahmen zu ergreifen und sich in etwas Sinnvolles zu verwandeln.
1 |
App.GroupChatComponent = Ember.Component.extend({ |
2 |
message: '', |
3 |
actions: { |
4 |
submit: function () { |
5 |
var message = this.get( 'message' ).trim(), |
6 |
conversation = this.$( 'ul' )[ 0 ]; |
7 |
|
8 |
// Fetches the value of 'action'
|
9 |
// and sends the action with the message
|
10 |
this.sendAction( 'action', message ); |
11 |
|
12 |
// When the Ember run loop is done
|
13 |
// scroll to the bottom
|
14 |
Ember.run.schedule( 'afterRender', function () { |
15 |
conversation.scrollTop = conversation.scrollHeight; |
16 |
});
|
17 |
|
18 |
// Reset the text message field
|
19 |
this.set( 'message', '' ); |
20 |
}
|
21 |
}
|
22 |
});
|
1 |
<form class="new-message" {{action submit on="submit"}}> |
2 |
{{input type="text" placeholder="Send new message" value=message class="txt-field"}}
|
3 |
{{input type="submit" class="send-btn" value="Send"}}
|
4 |
</form>
|
Da die Gruppen-Chat-Komponente die Schaltfläche Eingabe und Senden besitzt, müssen wir auf den Benutzer reagieren, der auf dieser Abstraktionsebene auf Senden klickt. Wenn der Benutzer auf die Schaltfläche "Senden" klickt, wird die Übermittlungsaktion in unserer Komponentenimplementierung ausgeführt. Innerhalb des Submit Action Handlers erhalten wir den Wert der Nachricht, der durch die Texteingabe festgelegt wird. Wir werden dann die Aktion zusammen mit der Nachricht senden. Schließlich setzen wir die Nachricht auf eine leere Zeichenfolge zurück.
Die andere seltsame Sache, die Sie hier sehen, ist die aufgerufene Ember.run.schedule-Methode. Dies ist wieder Embers Run-Loop 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 es verwaltet, wobei Render eine davon ist. In unserem Fall sagen wir also, wenn das Senden der Nachricht mit Manipulationen abgeschlossen ist und nachdem die Renderwarteschlange geleert wurde, rufen Sie unseren Rückruf an. Dadurch wird unsere ul nach unten gescrollt, sodass der Benutzer die neue Nachricht nach Manipulationen sehen kann. Um mehr über die Run-Schleife zu erfahren, empfehle ich, Alex Matchneers Artikel "Alles, was Sie nie über die Ember-Run-Schleife wissen wollten" zu lesen.
Wenn wir zum Browser gehen und auf die Schaltfläche "Senden" klicken, wird von Ember eine wirklich nette Fehlermeldung angezeigt: "Nicht erfasster Fehler: Das Ereignis 'sendMessage' wurde nicht behandelt. Dies erwarten wir, da wir unserer Anwendung nicht mitgeteilt haben, wie sie auf diese Art von Ereignissen reagieren soll. Lassen Sie uns das beheben.
1 |
App.IndexRoute = Ember.Route.extend({ |
2 |
/* … */
|
3 |
actions: { |
4 |
sendMessage: function ( message ) { |
5 |
if ( message !== '') { |
6 |
console.log( message ); |
7 |
}
|
8 |
}
|
9 |
}
|
10 |
});
|
Wenn wir nun zum Browser zurückkehren und 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 damit etwas interessanteres machen. Zuerst erstellen wir ein neues Ember.Object, das als Modell für eine neue Nachricht dient.
1 |
App.Message = Ember.Object.extend({ |
2 |
id: 3, |
3 |
firstName: 'Chad', |
4 |
lastName: 'Hietala', |
5 |
twitterUserName: 'chadhietala', |
6 |
text: null, |
7 |
timeStamp: null |
8 |
});
|
Wenn die Aktion sendMessage ausgeführt wird, möchten wir das Text- und timeStamp feld unseres Nachrichtenmodells füllen, eine neue Instanz davon erstellen und diese Instanz dann in die vorhandene Nachrichtensammlung verschieben.
1 |
App.IndexRoute = Ember.Route.extend({ |
2 |
/* … */
|
3 |
actions: { |
4 |
sendMessage: function ( message ) { |
5 |
var user, messages, newMessage; |
6 |
|
7 |
if ( message !== '' ) { |
8 |
|
9 |
messages = this.modelFor( 'index' ), |
10 |
newMessage = App.Message.create({ |
11 |
text: message, |
12 |
timeStamp: Date.now() |
13 |
})
|
14 |
|
15 |
messages.pushObject( newMessage ); |
16 |
}
|
17 |
}
|
18 |
}
|
19 |
});
|
Wenn wir zum Browser zurückkehren, sollten wir jetzt in der Lage sein, neue Nachrichten zu erstellen.



Wir haben jetzt mehrere 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 müssen, können wir die Benutzer-Avatar-Komponente wiederverwenden.
1 |
<script type="text/x-handlebars" data-template-name="index"> |
2 |
...
|
3 |
{{user-avatar username="horse_js" service="twitter" }} |
4 |
{{user-avatar username="detroitlionsnfl" service="instagram" }} |
5 |
{{user-avatar username="KarlTheFog" service="twitter" }} |
6 |
</script>
|



JQuery-Plugins einpacken
An diesem Punkt fragen Sie sich wahrscheinlich: "Was ist, wenn ich ein jQuery-Plugin in meiner Komponente verwenden möchte?" Kein Problem. Der Kürze halber ändern wir unsere Benutzer-Avatar-Komponente so, dass ein Werkzeugtipp angezeigt wird, wenn wir den Mauszeiger über den Avatar bewegen. Ich habe mich für den tooltipster des jQuery-Plugins entschieden, um den Werkzeugtip zu verarbeiten. Lassen Sie uns den vorhandenen Code ändern, um den tooltipster zu verwenden.
Fügen wir zunächst korrekte Dateien zu unserer chat.html hinzu und ändern Sie die vorhandene Benutzer-Avatar-Komponente.
1 |
... |
2 |
<link href="css/tooltipster.css" rel="stylesheet" /> |
3 |
|
4 |
... |
5 |
<script type="text/JavaScript" src="js/libs/jquery.tooltipster.min.js"></script> |
6 |
<script type="text/JavaScript" src="js/app.js"></script> |
7 |
... |
Und dann unser JavaScript:
1 |
App.UserAvatarComponent = Ember.Component.extend({ |
2 |
/*…*/
|
3 |
setupTooltip: function () { |
4 |
this.$( '.avatar' ).tooltipster({ |
5 |
animation: 'fade' |
6 |
});
|
7 |
}.on( 'didInsertElement' ), |
8 |
|
9 |
destroyTooltip: function () { |
10 |
this.$( '.avatar' ).tooltipster( 'destroy' ); |
11 |
}.on( 'willDestroyElement' ) |
12 |
|
13 |
)};
|
Wir sehen wieder die Syntax des deklarativen Ereignis-Listeners, aber zum ersten Mal sehen wir this.$. Wenn Sie mit jQuery vertraut sind, würden Sie erwarten, dass wir alle Elemente mit der Klasse 'Avatar' abfragen. Dies ist in Ember nicht der Fall, da der Kontext angewendet wird. In unserem Fall suchen wir nur nach Elementen mit der Klasse 'Avatar' in der Benutzer-Avatar-Komponente. Es ist vergleichbar mit der Suchmethode von jQuery. Bei der Zerstörung des Elements sollten wir das Schwebeereignis auf dem Avatar aufheben und alle Funktionen bereinigen. Dies geschieht durch Übergabe von "Zerstören" an den Werkzeugtipp. Wenn wir zum Browser gehen, ein Bild aktualisieren und mit der Maus darüber fahren, sollte der Benutzername des Benutzers angezeigt werden.



Abschluss
In diesem Tutorial haben wir uns eingehend mit Ember-Komponenten befasst und gezeigt, wie Sie wiederverwendbare Teile der Benutzeroberfläche verwenden können, um größere Composites zu generieren und jQuery-Plugins zu integrieren. Wir haben uns angesehen, wie sich Komponenten von Ansichten in Ember unterscheiden. Wir haben auch die Idee der schnittstellenbasierten Programmierung in Bezug auf Komponenten behandelt. Hoffentlich konnte ich nicht nur Ember Components, sondern auch Web Components und die Richtung des Web beleuchten.



