Anzeige
Anzeige
UX & Design
Artikel merken

Single-Page-Applikationen mit dem JavaScript-Framework entwickeln: Ember.js im Einsatz

Mit Ember.js lassen sich Single-Page-Apps entwickeln, die in ihrer Funktionalität Desktop-Anwendungen ähneln – nur mit dem feinen Unterschied, dass sie im Browser ablaufen. Eine kleine Beispiel-Anwendung zeigt, wie die Entwicklung mit dem Framework abläuft.

7 Min. Lesezeit
Anzeige
Anzeige

Single-Page-Applikationen laden nur eine einzige HTML-Site vom Server. Diese enthält – bis auf dynamische Inhalte – alles, was zum Anzeigen der gesamten Anwendung notwendig ist. Der Benutzer merkt davon nichts: Er sieht immer nur den gerade für ihn relevanten Teil der Anwendung, etwa den Bildschirm zum Anmelden. Unter der Haube sind aber auch schon alle anderen Teile der App in den Browser geladen – allerdings sind sie nur bei Bedarf zu sehen. Falls die Anwendung keine weiteren Ajax-Abfragen benötigt, kann der
Anwender ohne Internetverbindung weiterarbeiten. Im Idealfall wirkt eine Single-Page-App also wie eine Desktopanwendung – mit dem Unterschied, dass sie im Browser abläuft.

Ember.js: Die Wurzeln des JavaScript-Frameworks

Anzeige
Anzeige

Ember.js ist aus SproutCore entstanden. SproutCore sollte die Entwicklung Desktop-ähnlicher Web-Anwendungen ermöglichen. Apple fördert dessen Entwicklung und setzte es auch für seine iCloud-Anwendungen ein. Doch SproutCore 1.0 galt als schwergewichtig: Es verwendete komplexe Komponenten anstelle von HTML. Die Erfahrungen mit diesem Ansatz waren gemischt [1].

Die Weiterentwicklung von SproutCore wich dann so stark vom ursprünglichen Konzept mit seinen Komponenten, Desktop-Look-and-Feel und vielem mehr ab, dass sich die Entwickler für einen neuen Namen entschieden: Ember.js. Nach zweijähriger Entwicklungszeit ist der Release Candidate 1 von Ember.js Mitte Februar 2013 erschienen.

Anzeige
Anzeige

Ember.js – mit jQuery und Handlebars als Basis

Ein Unterschied von Ember.js im Vergleich zu anderen JavaScript-MVC-Frameworks ist, dass Ember.js auf jQuery und Handlebars basiert. Handlebars.js ist eine (noch) nicht so bekannte Erweiterung der Template-Engine Mustache. Ansonsten ist Ember.js neutral: Man kann das Framework problemlos mit jQueryUI, Twitter Bootstrap oder Zurb Foundation einsetzen. Zu Beginn jeder Ember.js-Anwendungsentwicklung muss man zunächst diese Bibliotheken in die HTML-Datei einbinden:

Anzeige
Anzeige
Die Datei index.html
<!DOCTYPE html>
<html lang="en">
<head>
 <!-- .... -->
</head>

<body>

 <!-- .... -->

 <script xsrc="js/jquery.js"></script>
 <script xsrc="js/handlebars-1.0.rc.2.js"></script>
 <script xsrc="js/ember.js"></script>
 <script xsrc="js/ember-data.js"></script>
 <script xsrc="js/app.js"></script>

</body>
</html>

Listing 1

Das Listing mitsamt allen Bibliotheken lässt sich auch im Netz herunterladen [2]. Zusätzlich zu den Bibliotheken kommt hier noch die Datei app.js dazu. Sie wird den folgenden Programmcode enthalten:

Die Datei app.js
(function () {
 "use strict";

 window.App = Ember.Application.create();

 App.Store = DS.Store.extend({
 revision:11,
 adapter:'DS.FixtureAdapter'
 });

 App.Router.map(function () {
  // Dir nachfolgende Route wird automatisch 
  // generiert, d.h. sie ist überflüssig
 this.route('index');

  // Die Route (=Zustand/Seite) für die Aufgaben
 this.route('tasks');
 });

})();

Listing 2

Die Klammer (function(){..})() um den Programmcode dient der Kapselung unseres Programmcodes. Man kann sie aber auch – ebenso wie die Anweisung use strict – zur strengeren Interpretation des JavaScript-Codes weggelassen.

Anzeige
Anzeige

Anschließend erzeugt die Anweisung Ember.Application.create() ein Applikationsobjekt. Es ist der Variable „App“ zugewiesen. Dadurch, dass die Variable an dem Window-Objekt hängt, kann man von überall aus auf sie zugreifen. Das Applikationsobjekt selbst ist ein Container für alle Ember-Objekte und -Klassen der Anwendung. Das heißt, alle nachfolgenden Objekte und Klassen liegen in App.

Die Anweisung App.Store = DS.Store.extend({..}) legt fest, welcher Datenspeicher zum Einsatz kommt. In den meisten Fällen ist dies der REST-Adapter. Das bedeutet, dass die Ember-App die Daten per REST-Aufruf vom Server lädt – und neue, geänderte oder gelöschte Datensätze per REST ans Backend sendet. Für unsere kleine Beispielanwendung verzichten wir auf ein Backendsystem und laden die Daten aus dem Programmcode (Fixtures). Änderungen lassen sich so natürlich nicht dauerhaft speichern.

Automatische Objekt-Generierung

Ein willkommenes Erbe aus der Ruby-on-Rails-Vergangenheit des Ember.js-Erfinders Yehuda Katz ist der Leitsatz „Convention over Configuration“. Er beschreibt die Idee, dass das Framework aufgrund von Namenskonventionen die verschiedenen Bestandteile einer Anwendung (Routing, Controller, Modelle, Views) sucht. Man muss diese also nur in Ausnahmefällen konfigurieren. Dieses Korsett gibt Ember.js-Anwendungen eine sehr klare Struktur und erleichtert normalerweise die Einarbeitung in fremde Anwendungen. Angenommen, das Beispiel-Applikationsobjekt heißt „App“ und in der Anwendung soll eine Aufgabe (englisch „Task“) abgebildet werden, dann heißen die beteiligten Objekte laut Konvention:

Anzeige
Anzeige
Namenskonventionen in Ember.js
Komponente Name Zweck
Router-Eintrag tasks Legt im Router das Mapping zwischen Zustand und URL fest
Modellklasse App.Task Legt fest, welche Attribute eine Instanz enthält
Fixture App.Task.FIXTURES Liefert Beispieldaten
Route App.TasksRoute Hier kann man Daten laden
Controller App.TasksController Enthält View-spezifische Methoden und Events
Template data-template-name =

"tasks"

Die Ansicht der Daten (liefert HTML)

Der Router-Eintrag existiert bereits, jetzt werden Model, Fixtures und Route in die app.js-Datei eingefügt. Der Beispielcode der entsprechend erweiterten Datei lässt sich unter folgendem Link herunterladen [3].

Die Model-Klasse legt fest, welche Attribute der Model Task hat (hier „name“ und „created_at“). Ohne dass man es angeben muss, erhält jede Klasse automatisch ein Attribut id mit dem Typ „integer“. Außerdem wird das berechnete Attribut „display_text“ definiert (mehr dazu unten). Die Fixtures definieren zwei Beispieldatensätze. Das heißt, die Beispiel-App generiert die Daten aus dem Sourcecode. In einem echten Projekt sollte man den REST-Adapter verwenden und die Daten aus einem Backendsystem laden.

Die Route-Klasse ist der richtige Ort, um Daten zu laden, die bei späteren Schritten benötigt werden (insbesondere dem View). Die Konvention ist hier, dass die Daten über die Methode „model“ zur Verfügung stehen. Die Beispiel-App lädt einfach alle Aufgaben (Task) ohne Einschränkung (App.Task.find ohne Parameter). In der Controller-Klasse könnte man View-spezifische Methoden oder Events realisieren. Diese benötigt diese App (noch) nicht; das heißt, der Controller entfällt. Aufgrund der Namenskonventionen weiß Ember.js jedoch, wie er heißen müsste. Wenn es ihn zur Laufzeit nicht findet, so generiert es einen Controller mit Standard-Verhalten. Diese automatische Objekt-Generierung ist einer der Gründe, warum Ember.js-Projekte einen so geringen Quelltext-Umfang haben. Klassen entstehen dabei im Hauptspeicher – und nicht wie bei Scaffolds in Ruby on Rails/Play/… über Dateien im Dateisystem.

Anzeige
Anzeige

Fehlt noch das Template zur Darstellung der Daten. Ember.js liefert zwei Arten von Templates: Application-Templates und modellspezifische Templates. In fast allen Anwendungen gibt es am Kopf und/oder Fuß der Seite Bereiche, die sich nicht verändern – etwa eine Navigation, Links zu den AGBs und Impressum. Damit man diese im Template nicht wiederholen muss, gibt es das Application-Template. Es hat entweder keinen Namen oder es heißt „application“.

Bei einem modellspezifischen Template (wie bei den Aufgaben) ist der Name (Attribut „data-template-name“) der Plural des Modell-Namens – hier also „tasks“. Dann sind die Templates in der index.html in Script-Tags vom Typ „text/x-handlebars“ eingebettet. Da der Browser diesen Typ des Script-Tags nicht kennt, rendert er ihn auch nicht. Bei Bedarf übernimmt Ember.js das Rendern.

Ein Template in der index.html-Datei
<script type="text/x-handlebars" data-template-name="tasks">
 <div class="row">
 <div class="span8">
 <h2>tasks to do</h2><br/></br/>
 {{#each controller}}
 <p>{{display_text}}</p>
 {{/each}}
 </div>
 </div>
</script>

Listing 3

Computed Properties

In dem Sourcecode der erweiterten app.js-Datei ist der Aufruf {{display_text}} zu finden. Bisher fehlt die Erklärung, was es mit der Funktion display_text in der Modell-Klasse „task“ auf sich hat:

Anzeige
Anzeige
Gut für die Performance: Die berechnete Eigenschaft display_text wird von Ember.js automatisch gecacht.
Gut für die Performance: Die berechnete Eigenschaft display_text wird von Ember.js automatisch gecacht.

Der rote Code-Teil ist einfach nur eine Komfort-Funktion für die Ausgabe einer Zeichenkette mit den wesentlichen Informationen einer Aufgabe. Der grüne Teil macht aus der Funktion eine Eigenschaft (Aufruf mit get(„display_text“)). Diese berechnet das Framework nur dann neu, wenn sich einer der Werte in der Klammer nach dem Keyword „property“ ändern. Ember.js liefert also ein vollautomatisches Caching.

Two-Way Data Binding

Eines der mächtigsten Features von Ember.js ist die beidseitige Datenbindung. Sie zeigt Änderungen an Modell-Klassen automatisch im Frontend an und ändert Inhalte verknüpfter Modelle, sobald der Benutzer auf der Website etwas ändert. Dieses Feature erspart Entwicklern enorm viel stupiden Programmcode („glue“). Es lohnt sich also, das Feature genauer zu betrachten.

Ruft man den Code unserer erstellten App [4] auf und wechselt zu /index.html#/tasks, bietet sich folgendes Bild:

Anzeige
Anzeige

Interessant wird es, wenn man die Google-Chrome-Entwicklerkonsole aufruft und folgenden JavaScript-Code eingibt:

JavaScript
App.Task.find(2).set("name", "do awesome stuff")

Listing 4

Anzeige
Anzeige

Wie der obere Screenshot zeigt, ändert sich automatisch die Bildschirmanzeige. Genauso lässt sich direkt ein weiterer Eintrag hinzufügen:

JavaScript
neu = App.Task.createRecord({
 name:"even more awesome stuff", 
 created_at:(new Date())})

Listing 5

Auch dieser Zusatz erscheint sofort im Frontend:

Wie ist das möglich? Was hat Ember.js aus dem Quelltext gemacht? Die Anzeige über einen Rechtsklick im Browser „Quelltext anzeigen“ bringt keinen Aufschluss, da sich der Code zur Laufzeit im Browser verändert und nicht – wie sonst üblich – auf der Serverseite. Klickt man in der Entwicklerkonsole auf die erste Tabseite (Elements), dann auf das Lupen-Symbol links unten und zuletzt auf das Element, um das es geht (hier den ersten Task-Eintrag), so wird ersichtlich: Ember.js hat um die einzelnen Einträge zwei
Script-Tags geschrieben (metamorph-n-start und metamorph-n-end). So kann das Framework diese nach dem Rendern leicht im
DOM-Baum des Browsers wiederfinden und ändern. Dazu verwendet es das Observer-Pattern. Jede Modell-Objekt-Änderung informiert ein Observer-Objekt, das eine Liste der Stellen
verwaltet, an denen dieses Objekt verwendet wird, und auch die zu aktualisierenden Eigenschaften verwaltet. Um Datensätze hinzuzufügen, gibt es einige
metamorph-n-start/end-Tags um die gesamte Liste.

Die IdentityMap

Noch erstaunlicher ist, dass die Veränderung (set("name", …)) eines Elements einer neugeladenen Liste (App.Task.find()) eine bereits dargestellte Oberfläche ändert. Schließlich ist die Datenquelle der Oberfläche eine andere Abfrage: Der Code in dem Route-Objekt. Möglich ist dies durch ein Feature in Ember.Data: Die IdentityMap. Ember.Data verwaltet eine Liste aller Objekte, die bisher geladen wurden. Ruft eine spätere Abfrage das gleiche Objekt nochmal ab, gibt Ember.Data nicht eine Kopie, sondern das identische Objekt zurück.

Fazit

Ember.js ist ein mächtiges Framework, das jedoch mit wenig Code auskommt. Die Maxime „Convention over Configuration“ sorgt für eine stärkere Strukturierung, als dies bei ähnlichen Frameworks wie Angular.js der Fall ist. Features wie Data-Binding oder die IdentityMap heben Ember.js von einfacheren Lösungen wie Backbone.js ab. Dazu kommen das mächtige Datenverwaltungskonzept Ember.Data, die RunLoop-Optimierung, die für sehr schnelle Oberflächenänderungen sorgt (auch bei alten Browsern) sowie zahlreiche weitere nützliche Funktionen.

In den vergangenen Wochen hat sich viel getan bei Ember.js. So haben die Gründer der Site StackOverflow Ember.js verwendet, um ihre neue Forensoftware discourse.org zu erstellen. Alle großen Verlage bringen Titel zu Ember.js heraus, die zum Teil auch schon als Beta/MEAP-Version veröffentlicht sind. Wer jetzt schon in die Entwicklung mit dem Framework einsteigen möchte, der sollte Community-Projekten wie „EmberWatch“, „EmberCasts“ oder „Discuss Ember.js“ einen Besuch abstatten.

Mehr zu diesem Thema
Fast fertig!

Bitte klicke auf den Link in der Bestätigungsmail, um deine Anmeldung abzuschließen.

Du willst noch weitere Infos zum Newsletter? Jetzt mehr erfahren

Anzeige
Anzeige
Schreib den ersten Kommentar!
Bitte beachte unsere Community-Richtlinien

Wir freuen uns über kontroverse Diskussionen, die gerne auch mal hitzig geführt werden dürfen. Beleidigende, grob anstößige, rassistische und strafrechtlich relevante Äußerungen und Beiträge tolerieren wir nicht. Bitte achte darauf, dass du keine Texte veröffentlichst, für die du keine ausdrückliche Erlaubnis des Urhebers hast. Ebenfalls nicht erlaubt ist der Missbrauch der Webangebote unter t3n.de als Werbeplattform. Die Nennung von Produktnamen, Herstellern, Dienstleistern und Websites ist nur dann zulässig, wenn damit nicht vorrangig der Zweck der Werbung verfolgt wird. Wir behalten uns vor, Beiträge, die diese Regeln verletzen, zu löschen und Accounts zeitweilig oder auf Dauer zu sperren.

Trotz all dieser notwendigen Regeln: Diskutiere kontrovers, sage anderen deine Meinung, trage mit weiterführenden Informationen zum Wissensaustausch bei, aber bleibe dabei fair und respektiere die Meinung anderer. Wir wünschen Dir viel Spaß mit den Webangeboten von t3n und freuen uns auf spannende Beiträge.

Dein t3n-Team

Melde dich mit deinem t3n Account an oder fülle die unteren Felder aus.

Bitte schalte deinen Adblocker für t3n.de aus!
Hallo und herzlich willkommen bei t3n!

Bitte schalte deinen Adblocker für t3n.de aus, um diesen Artikel zu lesen.

Wir sind ein unabhängiger Publisher mit einem Team von mehr als 75 fantastischen Menschen, aber ohne riesigen Konzern im Rücken. Banner und ähnliche Werbemittel sind für unsere Finanzierung sehr wichtig.

Schon jetzt und im Namen der gesamten t3n-Crew: vielen Dank für deine Unterstützung! 🙌

Deine t3n-Crew

Anleitung zur Deaktivierung
Artikel merken

Bitte melde dich an, um diesen Artikel in deiner persönlichen Merkliste auf t3n zu speichern.

Jetzt registrieren und merken

Du hast schon einen t3n-Account? Hier anmelden

oder
Auf Mastodon teilen

Gib die URL deiner Mastodon-Instanz ein, um den Artikel zu teilen.

Anzeige
Anzeige