Ember.js

I like Ember.js

Es gibt viele JavaScript basierte Frameworks um Single Page Applications zu realisieren. Die bekanntesten darunter sind Backbone, Knockout, Angular und Ember. Gefühlt gibt es mit Sicherheit noch weitere hunderte von solchen Bibliotheken und Frameworks, aber diese vier sind letztlich mir im Gedächtnis geblieben.

Single Page Applications geben mir die Möglichkeit mit einer Anwendung möglichst viele mobile Endgeräte zu erreichen.

SPA für Windows Phone 8

Ohne das ich mich hier auf iOS, Android oder Windows Phone spezialisieren muss, kann ich eine Anwendung auf Basis von JavaScript schreiben und diese auf alle Geräte deployen. Soweit die Theorie.

Ich habe mir Ember.js herausgepickt um ein kleines Projekt für Smartphones zu schreiben. Bevor ich mit dem Projekt starte habe ich einfach mal die verschiedenen Konzepte des Frameworks versucht mit einem ganz einfachen Durchstich zu realisieren.

WAMS Integration wichtig

Dabei war mir wichtig Daten im Backend zu speichern. Hierfür nehme ich Windows Azure Mobile Services her, diese bieten seit dem letzten Update nun auch die Möglichkeit JavaScript basierte Anwendung auf die API zugreifen zu lassen. Eine Liste von zugelassenen Hostnamen macht dies möglich.

Proof of Concept

Für den Durchstich möchte ich folgendes erreichen: Einfache Navigation zwischen zwei Views. Anzeigen von Daten aus dem WAMS Backend. Speichern von Daten in den WAMS Backend. Das erste was ich gemacht habe ist mir das aktuellste Starter Kit von Ember.js zu installieren. Momentan befindet es sich in der Version 1.0.0 RC3 und bringt JavaScript Dateien von Ember.js, Handlebars und jQuery mit.

Ember-Data wäre die Wahl für die Integration der Datenschicht. Im Moment befindet es sich allerdings in “Breaking Development”

Normalerweise wäre noch Ember-Data mit im Spiel. Es wird momentan sehr viel daran entwickelt und befindet sich noch zu sehr im Fluss als das ich es für den kleinen Proof Of Concept in Betracht ziehen kann. Sobald es hier eine stabilere Version gibt ziehe ich Ember-Data mit ins Projekt.

Ember.js in Action

Um mit Ember.js zu starten zieht man sich, wie bereits erwähnt, die JavaScript Referenzen der Bibliotheken in seine Startseite.

  <script src="js/libs/jquery-1.9.1.js"></script>
  <script src="js/libs/handlebars-1.0.0-rc.3.js"></script>
  <script src="js/libs/ember-1.0.0-rc.3.js"></script>

Zusätzlich referenziert man nun die JavaScript Datei die die initiale Anwendungslogik enthält. In dem Beispiel ist dies die Datei  `app.js`

  <script src="js/app.js"></script>

In der `app.js` wird die Anwendung  instanziert. Mit dieser Instanz ist das Ember.js Anwendungsframework  initiiert . Damit auch einige Standards und Konventionen.

var App = Ember.Application.create();

Die definierte Standardroute ist `index`. Der Inhalt der HTML Seite wird angezeigt und enthaltene Handlebar Templates gerendert. Enthält die Seite das `{{outlet}}` Template wird hier das Ergebnis einer eigenen Index Route / Controllers / Views als Ergebnis dargestellt.

  <script type="text/x-handlebars">
    <h2>leihnix</h2>

    {{outlet}}
  </script>

Diese Template Definition lässt sich mit folgendem View Template füllen:

  <script type="text/x-handlebars" data-template-name="index">
    <div>{{#linkTo lent}} ausleihen {{/linkTo}}</div>
    <div>{{#linkTo about}} about {{/linkTo}}</div>
    <div id="output"></div>
    {{view App.ListView}}
  </script>

Dabei wird die Verbindung zur Index Route weder über eine definierte Route noch über einen Controller hergestellt sondern lediglich über das `data-template-name` Attribut, welches eben `index` lautet.

Convention over Configuration

Diese Standard Mechanismen erleichtern die Entwicklung, vorausgesetzt man ist bereit sich diese anzueignen. Das ist meines Erachtens die größte Hürde um mit Ember.js in Fahrt zu kommen. Die investierte Zeit ist gut angelegt, kennt man sich aus, kommt man schnell zu Ergebnissen. Wobei das auf alle Technologien zutrifft.

Beim vorherigen Template ist ein Template Helper enthalten: `{{#linkTo}}`. Dieser Helper erstellt eine URL anhand einer Routendefinition.

App.Router.map(function() {
  this.route("about");
  this.route("lent");
});

Mit der Routendefinition definiert man Einstiegspunkte für Anwendungslogik, ob Route, Controller, View, Handlebar-Template, alles ist über den Namen der Route verknüpft.

App.Router.map(function() {
  this.route("funny");
});

App.FunnyRoute = Ember.Route.extend({
});

App.FunnyController = Ember.Controller.extend({
});

<script type="text/x-handlebars" data-template-member="funny">
  <strong>blub</strong>
</script><

Startseite

Die Startseite in dem Beispiel Projekt enthält  eine weitere Template direktive `{{view App.ListView}}`.

App.ListView ist eine  View Implementierung, die eigentlich nichts weiter macht als ein Template anzuzeigen. Beim ursprünglichen Ausprobieren hatte ich hier noch weiteren Code enthalten den ich aber rausgeworfen habe. Im Moment wird nur ein Template im View referenziert, eigentlich Overkill, aber egal.

App.ListView = Ember.View.extend({
    templateName: "overview-view",
})
<script type="text/x-handlebars" data-template-name="overview-view">
        <h3>ausgeliehen</h3>
        <ul>
        {{#each App.indexController}}
            <li>{{what}} to {{whom}} on {{when}}</li>
        {{/each}}
        </ul>
  </script>

Das Template iteriert nun über einen ArrayController um die Inhalte des Datenbackends anzuzeigen. Zur Implementierung komme ich noch. Vorher möchte ich  über die beiden weiteren Views sprechen die in der Beispiel Anwendung enthalten sind, `lent` und `about`.

Dateneingabe

Die `about` View ist nichts weiter wie ein statisches Template. Anders sieht es hier bei `lent`aus. Hier werden die Anwendungsdaten erfasst. Die Beispiel Anwendung soll einfach Dinge die man verleiht protokollieren. An Wen habe ich Was Wann verliehen. Das ist es schon. Ein solches Verleihobjekt gibt es auch als Ember Objekt definiert.

App.LeihObjekt = Ember.Object.extend({
    what: "",
    when: "",
    whom: ""
});

Idealerweise definiert man ein Model das man sogleich mit Ember-Data nutzen kann, da ich aus besagtem Grund das nicht in diesem POC einsetze habe ich mich für ein normales Objekt entschieden. Die `lent` View enthält eine weitere View mit den Eingabefeldern

  <script type="text/x-handlebars" data-template-name="entry-view">
    <div>
        <label>what</label>
        {{view Ember.TextField valueBinding="what"}}
    <div>
    <div>
        <label>when</label>
        {{view Ember.TextField valueBinding="when"}}
    <div>
    <div>
        <label>whom</label>
        {{view Ember.TextField valueBinding="whom"}}
    <div>
    <button {{action save}}>save</button>
  </script>

Das Speichern der Daten erfolgt dann im Controller. Hier setze ich bereits Windows Azure Mobile Services ein und speichere asynchron die Daten in den Backend.

App.LentController = Ember.Controller.extend({
    what: "irgendwas",
    when: new Date(),
    whom: "an wen?",
    save: function(name) {
        var that = this;
        var verleihen = App.LeihObjekt.create({
            what: this.get('what'),
            when: this.get('when'),
            whom: this.get('whom')
        });
        table.insert(verleihen).done(function() {
            App.indexController.pushObject(verleihen);
            that.transitionToRoute("index");
        });
    }
});

Die Funktion `save` des Controllers ist per Template Helper `action` gebunden. Die  Framework View `Ember.TextField` sorgt  für das Rendering der entsprechenden Eingabeelemente und die Bindung an das Eigenschaftsfeld.

WAMS Anbindung

Die Speicherung der Daten wird über den `insert` getriggert. Die Funktion ist asynchron und liefert einen Promise zurück. Im Callback wird nun einem ArrayController das erfolgreich gespeicherte Objekt hinzugefügt und die Ergebnisliste auf der Startseite wird automatisch notifiziert das sich Daten geändert haben und der View sich entsprechend aktualisiert.

Die Serverseitige Anbindung wird beim Starten der Anwendung vollzogen:

var client;
var table;

$(function() {
    client = new WindowsAzure.MobileServiceClient(
        "https://ember.azure-mobile.net/",
        "lkaBHxWcVIuUttnjoSvnDhanONFqZp98"
    );

    table = client.getTable('Leihnix');
});

Das ist die Basis um nun Daten im Azure Backend zu speichern und zu lesen.

ArrayController

Die Inhalte der Liste werden über einen ArrayController zur Verfügung gestellt. Sobald die Indexseite gerendert wird, ruft der Controller in den Azure Backend und holt sich dort die Daten ab. Im Promise werden diese Daten auf den Controller gesetzt.

App.indexController = Ember.ArrayController.create();

App.IndexController = Ember.ArrayController.extend({
    init: function() {
        table.read().then(function(items) {
            App.indexController.set('content', items);
        });
    }
})

Neu hinzugefügte Datensätze werden ins Backend geschrieben und dann einfach der Liste im ArrayController hinzugefügt ohne nochmaliges lesen aus dem Backend.

Ember.js auf Windows Phone 8

Um den Durchstich noch final zu beantworten habe ich die Dateien in eine Windows Phone 8 HTML5 Anwendung gepackt. Dabei handelt es sich um ein C# Projekt Template welches ein Web Browser Control hostet. Kurz gestartet und funktionalität evaluiert. Tut. Damit ist die Basis für mein Projekt gelegt und ich kann die Paketierung der Web App später über ein Tool wie z.B. Phonegap machen um zusätzlich zum Windows Phone 8 auch Android und iOS zu adressieren.

I Like Ember.js

Alles in allem sind die ersten kleinen Schritte mit Ember.js Lernaufwendig gewesen, aber von einem ersten Gefühl her würde ich sagen das es durchaus großes Potential hat. Zumal auch ein bekanntes großes Open Source Projekt wie Discoure.org darauf basiert.

Den Code von meinem POC habe ich hier auf Github publiziert.

Kategorien:Coding

Getagged als:, ,

Follow

Erhalte jeden neuen Beitrag in deinen Posteingang.

Schließe dich 797 Followern an

%d Bloggern gefällt das: