Knockout.js: Performante Web-Apps – einfach wie nie
Knockout.js ist eine JavaScript-Bibliothek für die Erstellung von agilen und interaktiven Web-Anwendungen. Hierbei bildet Knockout.js das Rückgrad deiner Web-Applikation und übernimmt sowohl einen großen Teil der Logik als auch die Ausgabe auf dem Bildschirm. Hierfür kommt MVVM (Model-View-View Model) zum Einsatz, was für eine enge Verknüpfung zwischen Daten und Ausgabe steht.
Model
Im Model werden die Daten und Operationen deiner Anwendung definiert. Zum Beispiel Buchungen mit den dazugehörigen Funktionen wie Bestätigung und Storno. Dabei ist das Model vollkommen getrennt vom eigentlichen User-Interface. Bei der Arbeit mit Knockout.js wirst du in der Regel AJAX-Anfragen an einen Server stellen, der die zum Model gehörende Daten speichert und ausgibt.
A model: your application’s stored data. This data represents objects and operations in your business domain (e.g., bank accounts that can perform money transfers) and is independent of any UI. When using KO, you will usually make Ajax calls to some server-side code to read and write this stored model data.
View-Model
Das View-Model repräsentiert die Daten und Operationen im Zusammenhang mit dem User-Interface. Wenn du zum Beispiel eine To-Do-Applikation erstellen würdest, würde das View-Model eine Liste mit den entsprechenden Aufgaben und Methoden um neue Aufgaben anzulegen oder erledigte Aufgaben zu entfernen beinhalten. Das View-Model beinhaltet alle (ungespeicherten) Daten, die während der Ausführung der Applikation erfasst und verändert werden.
View
Der View ist ein sichtbares und interaktives User-Interface, das den Zustand der View-Models repräsentiert. Es zeigt Informationen aus dem View-Model an, schickt Anweisungen ab (beispielsweise beim Klick auf einen Button) und wird aktualisiert, sobald sich etwas im View-Model verändert. Bei der Arbeit mit Knockout.js handelt es sich bei der View um dein HTML-Dokument mit entsprechenden Bindings, die eine Verknüpfung zum View-Model herstellen.
Das Knockout-View-Model
Knockout.js fasst die Programmlogik in so genannten View-Models zusammen, die einem View (HTML, zum Beispiel der gesamte document
-Body oder nur ein interaktiver Bereich der Seite) zugewiesen werden können.
var myViewModel = {
personName: 'Bob',
personLastName: 'Odenkirk',
personAge: 23
};
ko.applyBindings(myViewModel);
Der Name des Users ist <span data-bind="text: personName"></span>
Das obenstehende Beispiel zeigt eine sehr grundlegende und fast schon statische Implementierung von Knockout.js. Das View-Model beschreibt einen Benutzer mit dessen Vornamen, Nachnamen und Alter. Mit der ko.applyBindings()
-Methode wird Knockout.js aktiviert. Der darauf folgende HTML-Ausschnitt stellt mit dem data-bind
-Attribut den Wert aus dem View-Model zur Verfügung, sodass die Ausgabe wie folgt lauten würde:
Der Name des Users ist <span>Bob</span>
Knockout.js: Observables
Besonders beeindruckend oder nützlich ist diese Implementierung nicht. Sein Potential entfaltet Knockout.js erst bei der Arbeit mit dynamischen Daten. Mit Hilfe von so genannten Observables kann Knockout.js blitzschnell größere Datenmengen verarbeiten.
Das Grundprinzip hierbei ist einfach: Ändert sich etwas an den Daten, ändert sich der Output. Wird der Output durch Benutzereingaben verändert, ändert sich sofort auch das Datenmodell. So bringst du etwas Dynamik in das obenstehende Beispiel:
var myViewModel = {
personName: ko.observable('Bob'),
personLastName: ko.observable('Odenkirk'),
personAge: ko.observable(23)
};
Um interaktiv mit den Daten arbeiten zu können, musst du das View-Model anpassen – der View selbst kann vollkommen unangetastet bleiben. Die kleine Änderung bringt gravierende Änderungen zum vorhergehenden Beispiel mit sich. Denn nun ist Knockout.js in der Lage, Änderungen an personName
, personLastName
und personAge
festzustellen und den View entsprechend anzupassen.
Observable-Werte: getter und setter
Aus Kompatibilitätsgründen (IE) sind Knockout-Observable-Objekte Funktionen und besitzen eigene Getter- und Setter-Methoden. Rufst du eine Observable ohne Parameter auf, erhältst du ihren Wert. Übergibst du einen Parameter, setzt du den Wert. So wird myViewModel.personName()
„Bob“ zurückgeben während myViewModel.personName('mary')
den Wert von personName
auf „Mary“ setzen wird.
Der Zweck von „Observables“ ist es, Variablen zu beobachten und auf Änderungen zu reagieren. Mit dem data-bind
-Attribut im obenstehenden HTML-Codebeispiel stellst du sicher, dass sich der HTML-Output bei Veränderung des Wertes der Observable personName
ändert – und das ohne selbst Funktionen für diesen Fall schreiben zu müssen.
Knockout.js: Computed Observables
Was wäre, wenn du nun den Vor- und Nachnamen des Benutzers ausgeben lassen willst? Du könntest folgendes HTML-Markup verwenden. um die Ausgabe zu erzielen:
Der Name des Users ist <span data-bind="text:userName"></span> <span data-bind="text: userLastName"></span>
In diesem Fall ist das durchaus akzeptabel. Bei komplexeren Aufgaben jedoch dürfte sich diese Methode als kaum praktikabel erweisen. Für diese und ähnliche Problemstellungen gibt es so genannte „Computed Observables“.
„Computed Observables“ sind Funktionen, die auf eine oder mehr „Observables“ zurückgreifen und so einen neuen Wert berechnen, jedes mal, wenn sich ein „Observable“ verändert. Im nachfolgenden Beispiel definieren wir ein View-Model mit „Observables“ für den Vor- und Nachnamen des Benutzers und eine „Computed Observable“ namens „fullName“, mit dessen Hilfe der vollständige Name des Users berechnet werden kann.
function AppViewModel() {
this.userFirstName = ko.observable('Bob');
this.userLastName = ko.observable('Odenkirk');
this.fullName = ko.computed(function() {
return this.userFirstName() + " " + this.lastName();
}, this);}
Der Zugriff auf den Wert des „Computed Observable“ im View erfolgt genau so wie der auf eine normale „Observable“.
Der Name des Users ist <span data-bind="text: fullName"></span>
„Computed Observables“ in Knockout beschränken sich nicht nur auf simple Aufgaben. Prinzipiell können sämtliche Operationen mit Observables des View-Models durchgeführt und so auch komplexere Berechnungen durchgeführt werden.
Knockout.js: Observable Arrays
Wenn du Änderungen an einem Objekt beobachten willst, benutzt du normale „Observables“. Wenn du auf Änderungen einer Sammlung von Objekten reagieren möchtest, brauchst du ein observableArray
. „Observable Arrays“ sind besonders bei Listen, Tabellen und ähnlichen Datenstrukturen, bei denen mehrfach Sektionen einer View wiederholt werden müssen, interessant.
Nehmen wir an, dass wir eine Liste mit CEOs bedeutender Tech-Unternehmen ausgeben wollen. Ein „Observable Array“ in unserem View-Model würde dann beispielsweise wie folgt aussehen:
var ceoList = ko.observableArray([
{ name: "Tim Cook", company: "Apple" },
{ name: "Larry Page", company: "Google" },
{ name: "Satya Nadella", comany: "Microsoft" }
]);
Das HTML-Markup für die Ausgabe würde wie folgt lauten:
<dl data-bind="foreach: ceoList">
<dt data-bind="text: name"></dt>
<dd data-bind="text: company“></dd>
</dl>
Die Ausgabe mit Knockout.js würde dann folgendermaßen aussehen:
<dl>
<dt>Tim Cook</dt>
<dd>Apple</dd>
<dt>Larry Page</dt>
<dd>Google</dd>
<dt>Satya Nadella</dt>
<dd>Microsoft</dd>
</dl>
Mit einem „Observable Array“ arbeiten
„Observable Arrays“ sind, wie auch „Observables“, jederzeit bereit, neue Werte aufzunehmen und die dazugehörigen „Listener“ über die Änderungen zu informieren, um beispielsweise DOM-Änderungen vorzunehmen. Dabei unterstützen „Observalbe Arrays“ von Arrays aus JavaScript bekannte Methoden wie pop
, push
, shift
, unshift
, reverse
, sort
und splice
, um Änderungen an der Array-Struktur vorzunehmen.
Würdest du nun also Mark Zuckerberg in der Definition List
sehen wollen, könntest du beispielsweise die push()
-Methode des „Observable Array“ verwenden.
seoList.push({name: “Mark Zuckerberg“, company: “Facebook“});
Benutzereingaben verarbeiten
Knockout.js kann nicht nur via JavaScript Änderungen an „Observables“ vornehmen und darauf reagieren: Auch können mit dem value
-Binding Formular-Elemente eine Verbindung zwischen der View und den „Observables“ herstellen.
Hierfür kann exemplarisch der View aus dem zweiten Beispiel mit dem Vor- und Nachnamen des Benutzers angepasst werden.
function AppViewModel() {
this.userFirstName = ko.observable('Bob');
this.userLastName = ko.observable('Odenkirk');
this.fullName = ko.computed(function() {
return this.userFirstName() + " " + this.userLastName();
}, this);}
myViewModel = new AppViewModel();
ko.applyBindings(myViewModel);
<input type="text" data-bind="value: userFirstName">
<input type="text" data-bind="value: „userLastName">
<span data-bind="text: fullName"></span>
Mit dem value
-Binding werden sämtliche Eingaben in den Textfeldern an Knockout.js übergeben, der neue Name des Benutzers durch „Computed Observable“ berechnet und im View ausgegeben.
Daten laden und speichern
Um das Speichern und Laden zu ermöglichen, bietet Knockout.js einen Datenaustausch im JSON-Format an. So kann die Applikation ihre Daten vollständig vom Server beziehen und jeweilige Benutzereingaben zur Speicherung oder Verarbeitung zurück übermitteln.
$.getJSON("/some/url", function(data) {
//Nutze die geladenen Daten um dein View Model zu
// aktualisieren. Knockout kümmert sich um das User Interface
})
var data = /* Daten für den Server */;
$.post("/some/url", data, function(returnedData) {
// Dieses Callback wird ausgeführt, wenn
// die Kommunikation mit dem Server erfolgreich war
})
Die View-Models in Knockout sind JavaScript-Objekte. Du könntest sie also im Prinzip mit jedem JSON-Serializer wie JSON.stringify
serialisieren und an den Server übermitteln. Da dein View-Model aber „Observables“, „Computed Observables“ und „Observable Arrays“ enthält, wird das nicht immer ohne Hilfe von deiner Seite sauber ablaufen können. Um diese Problematik zu umgehen, bietet Knockout zwei Helper-Funktionen an.
ko.toJS
klont dein View-Model und ersetzt alle „Observables“ mit den aktuellen Wert, sodass du eine saubere Kopie der Daten deines View-Models erhältst. ko.toJSON
macht das gleiche im JSON-Format, sofern der Browser einen integrierten JSON-Serializer hat.
var viewModel = {
firstName : ko.observable("Bert"),
lastName : ko.observable("Smith"),
pets : ko.observableArray(["Cat", "Dog", "Fish"]),
type : "Customer"
};
viewModel.hasALotOfPets = ko.computed(function() {
return this.pets().length > 2
}, viewModel)
var jsonData = ko.toJSON(viewModel);
'{"firstName":"Bert","lastName":"Smith","pets":["Cat","Dog","Fish"],"type":"Customer","hasALotOfPets":true}'
Um vom Server geladene Daten für das View-Model zu benutzen, kann eine einfache Zuweisung über die Setter-Methoden der „Observables“ erfolgen.
// JSON laden und Parsen
var someJSON = /* JSON String von der Datenquelle deiner Wahl */;
var parsed = JSON.parse(someJSON);
// View Model aktualisieren
viewModel.firstName(parsed.firstName);
viewModel.pets(parsed.pets);
In vielen Szenarien ist das die einfachste Herangehensweise. Wenn das gesamte View-Model nicht von vornherein händisch definiert werden soll, werden andere, fortgeschrittene Methoden nötig. Hierfür stellt Knockout.js das knockout.mapping
-Plugin zur Verfügung, mit dem die Menge an Code drastisch reduziert werden kann. Diese Methode empfiehlt sich für komplexere Anwendungen und fortgeschrittene Benutzer und wird daher in diesem Artikel nicht näher behandelt.
Fazit
Dieser Artikel deckt nur die Basis der Möglichkeiten ab, die sich mit Knockout.js eröffnen. Neben den Text- und Value-Bindings bietet Knockout noch eine Reihe weiterer Bindings für Attribute, HTML, CSS, Sichtbarkeit und Events an, die den Rahmen dieses Artikels deutlich sprengen würden.
Die Möglichkeiten, die Flexibilität und die Einfachheit im Umgang mit Knockout.js sprechen jedoch für sich. Mit Knockout.js kannst du schnell und unkompliziert komplexe Web-Anwendungen schreiben, brauchst deutlich weniger Code und Zeit als mit eigenen „Listenern“/Methoden und stellst in Sachen Performance und Komplexität sogar die ein oder andere clientseitige Template-Engine in den Schatten.
Die ersten Schritte mit Knockout.js können holprig sein, doch wenn die Grundlagen erst mal sitzen, will man gar nicht mehr darauf verzichten. Die interaktiven Knockout.js-Tutorials auf der Herstellerseite bringen dir die Materie schrittweise und gut verständlich näher.
Auf der offiziellen Webseite von Knockout.js erfährst du mehr über die JavaScript-Bibliothek und kannst mit den ersten Experimenten beginnen.
Du möchtest wissen, wie OKR deine Unternehmensführung modernisieren kann? Dann melde dich zum Seminar „OKR ganzheitlich verstehen und anwenden“ an! Auf 20 Teilnehmende limitiert!
Mhn, wenn ich die Syntax so sehe, dann finde ich Angular irgendwie sympatischer :)
Das habe ich mir auch gedacht :D
fullack :D
Ein kurzer Vergleich zu AngularJS wäre hilfreich.
Alles schön und gut, leider beschränken sich fast alle Frameworks aufs Databinding und etwas MVC (oder ähnliches) und lösen nicht die Datenspeicherung. Ich finde Hoodie (http://hood.ie/) sehr interessant, das eignet sich durch die Anbindung an CouchDB auch gut für dezentrale Software wie Apps. Ich habe bisher nur ein paar Vorträge zu dem Thema gesehen und die Software selbst scheint noch in der Entwicklung zu stecken, aber vielleicht ist das ja mal einen Artikel wert. Soweit ich mitbekommen habe gibt es dort kein Databinding also ließe es sich wahrscheinlich auch mit Knockout & Co kombinieren.