JavaScript-Workshop
Grundlagen, OOP, jQuery, Backbone.js, MVC, Chaplin.js
Mathias Schäfer
- Software-Entwickler bei 9elements
- Studium der Germanistik und Philosophie
- Spezialisiert auf Frontend-Techniken HTML, CSS und JavaScript
- Autor von Fachartikeln und Dokumentationen (u.a. SELFHTML, Webkrauts)
- Über 10 Jahre JavaScript-Erfahrung
- Initiator von Chaplin.js
- Bochum & Berlin
- Design, Interfaces, Programmierung
- früher Flash- und Spiele-Entwicklung
- Webanwendungen mit Ruby on Rails
- HTML5- und JavaScript-Apps
- iOS- und Android-Entwicklung
Programm
Sprachgrundlagen
JavaScript in HTML, jQuery
Strukturierung: OOP und funktionale Programmierung
Anwendungen mit Backbone.js und Chaplin.js
Werkzeuge
- Editor (Sublime, WebStorm, Aptana, Zend)
- Browser mit Konsole und Debugger (Chrome/Web Inspector, Firefox/Firebug)
- Webserver (Apache, nginx)
- Git und das Schulungs-Repository
- EtherPad
Ablauf
- Vortrag mit Fragen und Antworten
- Aufgaben und Beispiele
- Umsetzungen besprechen, Refactoring
- Mitmachen und Ausprobieren auf der Konsole
Was ist JavaScript?
- Eine interpretierte, dynamische, multiparadigmatische, universell einsetzbare Programmiersprache
- Schwach und dynamisch typisiert
- Durch Lisp und Scheme beeinflusst
Geschichte
- Netscape Navigator 2.0, 1995
- Scripting im Web-Browser
- Beginn des kommerziellen, allgemein zugänglichen WWW
- Usprünglich zum Scripting von Java-Applets konzipiert
- »Java is to JavaScript as ham is to hamster«
Entwicklung
- Andere Browser implementierten JavaScript
- Standardisierung als ECMAScript
- Edition 1: 1997
- Edition 5.1: 2011
- Edition 6: in Entwicklung
- Standardisierung der Browser-APIs
- Buzzwords: DHTML, Ajax, HTML5…
Verbreitung
- Open-Web-Plattform(HTML, CSS, JavaScript, DOM, HTTP, XML, JSON, SVG, PNG, JPEG etc.)
- Node.js, CommonJS-Implementierungen
- webOS, Firefox OS
- Desktop-Widgets, Windows-8-Apps
Heutiger Stand
- JavaScript ist überall und gewinnt an Einfluss
- »The World’s Most Popular Programming Language«
- Ausgereifte Interpreter von Apple, Microsoft, Google, Mozilla, Opera
- Davon mehrere Open Source (V8, SpiderMonkey, JavaScriptCore, Rhino)
JavaScript-Grundlagen
window.alert("Hello World!");
JavaScript-Grundlagen
window.alert("Hello World!");
- Objekt window
- Eigenschaft alert
- Eigenschaftswert: Funktion
- Funktionsaufruf ()
- String-Literal "Hello World!" als Funktionsparameter
- Semikolon am Zeilenende
Syntax
Programmcode besteht aus…
Statements (u.a. Kontrollstrukturen)
Expressions (Ausdrücke)
Bezeichner, Literale und Operatoren
vgl. Text – Abschnitt – Satz – Satzglied –Wort – Morphem – Buchstabe
Statements
- »Expression Statements« (Ausdrücke)
- window.alert("Hello World!");
- Variablen-Deklarationen
- Bedingte Anweisungen (if-else, switch)
- Schleifen (while, do-while, for, for-in; continue, break)
- Ausnahmebehandlung (try-catch-finally/throw)
Variablen
var one = 1;
var two = 2;
var three = one + two,
four = 4,
result = three + four;
alert(result);
Variablen
Globale Variablen sind Eigenschaften des globalen Objekts window:
var one = 1;
alert(one); // 1
alert(window.one); // 1
Gundregel: Globale Variablen vermeiden.
JSLint/JSHint warnt vor globalen Variablen.
Schleifen und Bedingungen
var ausgabe;
for (var i = 1; i <= 10; i++) {
if (i % 2 == 0) {
ausgabe = "gerade";
} else {
ausgabe = "ungerade";
}
alert(i + " ist " + ausgabe);
}
var name = "";
while (name == "") {
name = window.prompt("Wie ist ihr Name?");
}
Syntax verstehen
Abstrakte Syntaxbäume (AST)
window.alert("Hello World!");
Program
ExpressionStatement
CallExpression
MemberExpression
Identifier: window
Identifier: alert
arguments:
Literal: "Hello World!"
Bspw. Esprima ECMAScript parser
Funktionen
Funktionsausdruck (Function Expression)
var add = function (a, b) {
return a + b;
};
var sum = add(2, 3);
Funktionsdeklaration (Function Declaration)
var sum = add(2, 3);
function add (a, b) {
return a + b;
}
Viele Fallstricke, daher besser vermeiden.
Funktionen
var add = function (a, b) {
return a + b;
};
var sum = add(2, 3);
Einfacher und konsistenter
Programmieraufgabe
Schreiben Sie eine Funktion, die eine positive ganze Zahl entgegennimmt und die Summe der ganzen Zahlen von 1 bis zur gegebenen Zahl zurückgibt.
Beispiel: 5
1 + 2 + 3 + 4 + 5 = 15
Umsetzungshilfe
- Funktion mit Parameter n
- Variable für Ergebnis
- Schleife, die von 1 bis n zählt
- Addition
- Rückgabewert
Mögliche Lösung
var sum = function (n) {
var result = 0;
for (var i = 1; i <= n; i++) {
result += i;
}
return result;
};
alert( sum(5) );
Lokale Variablen
var max = function (list) {
var i, l = list.length, biggest = 0, candidate;
for (i = 0; i < l; i++) {
candidate = list[i];
if (candidate > biggest) {
biggest = candidate;
}
}
return biggest;
};
alert( max( [65, 24, 98, 44] ) ); // 98
alert(biggest); // ReferenceError: max is not defined
Grundlegende Datentypen
- Einfache Werte (Primitives)
- Boolean, Number, String
-
undefined, null
- Object
- Array
- Function
- RegExp, Date
Einfache Werte
- Boolean: true, false
- Number: 0, 1, 2, 3.75, -56.1, 1e6, 0x10
- String: "Hühnerstall", 'Motorrad'
Einfache Werte
Verhalten sich wie Objekte, besitzen Methoden:
(2.5172).toFixed(2) // 2.52
"Hello World".indexOf("World") // 6
"Hello World".charAt(6) // "W"
"Hello World".substring(6, 11) // "World"
"Hello World".replace("World", "there") // "Hello there"
Arrays
Listen von Werten beliebigen Typs
var array = [1, 2, 3];
alert(array); // 1,2,3
alert(array.join(',')); // 1,2,3
alert(array.length); // 3
alert(array[0]); // 1
alert(array.indexOf(3)); // 2
Arrays
var array = [1, 2, 3];
// Iteration mit for-Schleife
for (var i = 0, l = array.length; i < l; i++) {
alert(i + ": " + array[i]);
}
// Iteration mit Callback-Funktion
array.forEach(function (el, i, array) {
alert(i + ": " + el);
});
// Listenoperationen (map, filter, reduce…)
var newArray = array.filter(function (el) {
return el > 1;
});
alert(newArray); // 2,3
Objekt-Literale
var objekt = {
eigenschaft: wert,
property: value,
…
};
alert(objekt.eigenschaft);
objekt.eigenschaft = …;
objekt.neueEigenschaft = …;
delete objekt.eigenschaft;
Hashes / Hash Maps / assoziative Arrays
Objekte
- Alles in JavaScript ist oder verhält sich wie ein Objekt
- Alle Objekte verhalten sich gleich
- Die meisten Objekte sind veränderbar
- Neue Eigenschaften können angelegt oder gelöscht werden
- Eigenschaften sind schreibbar
- Objekte sind sowohl Arbeitsdaten als auch Programmstruktur
Objekt-Literale
var person = {
name: 'Mathias',
postcode: 44793,
employer: '9elements',
languages: ['JavaScript', 'Ruby'],
greet: function () {
alert('Hallo von ' + this.name);
}
};
person.greet();
Eines der mächtigsten Features von JavaScript
JSON
JavaScript Object Notation
Untermenge von JavaScript
[
{ "name": "Mathias", "postcode": 44793,
"languages": [ "JavaScript", "Ruby" ] },
{ "name": "Lena", "postcode": 10711,
"languages": [ "Objective-C", "Java" ] }
]
JSON.stringify() und JSON.parse()
JavaScript in HTML
<script>
window.alert("Hello World!");
</script>
<script src="script.js"></script>
JavaScript in HTML5
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>Titel</title>
<link rel="stylesheet" src="style.css">
</head>
<body>
<h1>Titel</h1>
<p>Inhalt</p>
<script src="script.js"></script>
</body>
</html>
»Put Stylesheets at the Top,Put Scripts at the Bottom«
JavaScript im Browser
- Kernobjekte aus ECMAScript
- Fenster (window)
- Dokumente (window.document)
- DOM-Knotenbaum (u.a. HTML-Elemente)
- Entwicklertools wie Firebug und DOM Inspector verwenden
DOM
- Document Object Model
- Die JavaScript-Schnittstelle zum HTML-Dokument, in dem das Script läuft
- Ein Baum von Knoten (Elemente, Text, Kommentare usw.)
DOM
<p id="content">Hello World</p>
var el = document.getElementById('content');
alert(el.nodeName); // P
alert(el.childNodes.length); // 1
alert(el.innerHTML); // Hello World
el.innerHTML = 'Hallo Welt!';
var strong = document.createElement('strong');
var text = document.createTextNode('Hallo Welt!');
strong.appendChild(text);
el.appendChild(strong);
- Ausgereiftes Standardtool für DOM- und Ajax-Operationen
- Das DOM ist umständlich und Low-Level
- jQuery bietet eine kompakte Syntax und ist browserübergreifend
Vorteile von jQuery
- Schneller Einstieg
- Knapper, verständlicher Code
- Häufige Aufgaben einfach lösen
- Etablierte gute Konventionen und vereinheitlicht Code
- Zahlreiche Erweiterungen (Plugins)
Grenzen von jQuery
- Deckt nur einen kleinen Bereich ab
- jQuery-Code skaliert nicht, wird schlecht wartbar
- jQuery bietet (fast) nichts zur Strukturierung
- Umfangreich und komplex
jQuery-Features
- Elemente mittels CSS-Selektoren finden
- DOM-Traversal (Bewegen im DOM-Baum)
- DOM-Manipulation (Elemente einfügen, Inhalte wechseln, Attribute setzen)
- CSS-Eigenschaften ändern, CSS-Animationen
- Event-Handling (z.B. Maus- und Tastaturevents), Event-Delegation
- HTTP-Anfragen an Server senden (Ajax)
jQuery-Grundlagen
$("#content")
- findet das Element mit der ID content
- gibt ein jQuery-Objekt zurück
- das ist eine Liste mit Elementobjekten (DOM-Knoten)
-
$() ist kurz für jQuery()
jQuery-Beispiel
<p id="content">Hello World</p>
<script>
alert( $('#content').length ); // 1
alert( $('#content')[0] ); // [object HTMLParagraphElement]
</script>
jQuery wrappt Elementobjekte in eine Listenobjekt mit vielen nützlichen Methoden
jQuery-Beispiel
<p id="content">Hello World</p>
alert( $('#content').html() ); // Hello World
$('#content').html('Hallo Welt!');
$('#content').click(function () {
$(this).html(text);
});
Beispiel: Bildergalerie
- Bildersuche bei Flickr
- Thumbnail-Liste
- Vollansicht bei Klick
Beispiel: Bildergalerie
GET-Request auf die JSON-API von Flickr
HTML für Ergebnisse zusammenbauen und ins DOM einfügen
Vollansicht bei Klick
JavaScripte strukturieren
Objektorientierte und funktionale Programmierung
Strukturierung
- ECMAScript 3 hat keine Klassen, Interfaces, Module, Traits, Mixins, Sichtbarkeit
- ECMAScript 3 hat Objekte, Funktionen, Konstruktoren, Prototypen
Neuere Standards
- ECMAScript 5 hat Getter/Setter, einfache Zugriffsrechte (Property Descriptors)
- ECMAScript 6 bekommt Module, Klassen als »Syntaxzucker«, private Eigenschaften, Proxies für Metaprogrammierung uvm.
Strukturierung
- ECMAScript 3 als Grundlage
- Module, Kapselung, Vererbung (Delegation), Pseudo-Klassen, Objekt-Komposition
- Imperativ statt deklarativ
- Konventionen und Code statt festen Sprachfeature
- Viele Möglichkeiten
Revealing Module Pattern
var modul = (function () {
var privateVariable = 1;
var privateFunktion = function () {…};
return {
öffentlicheEigenschaft: 1,
öffentlicheMethode: function () {…}
};
})();
Revealing Module Pattern
- Funktions-Scope für private Daten und Funktionen
- Selbstausführende Funktion mit Closures
- Gibt einen Object-Literal mit öffentlichen Eigenschaften und Methoden zurück
Demonstration
Prototypen
- Jedes Objekt hat einen Prototypen-Verweis
- Der Prototyp ist ein normales Objekt
- Wenn eine Eigenschaft nicht gefunden wird, wird sie beim Prototypen gesucht (Delegation)
- So bekommen Objekte die Fähigkeiten beliebig vieler anderer Objekte
Prototypen
- JavaScript basiert intern auf Prototypen
- Die meisten Objekte erben ihre Methoden von Prototypen
- Die Prototypen der Kernobjekte sind lesbar und erweiterbar
- Object.prototype, Function.prototype, Array.prototype, String.prototype, HTMLDocument.prototype, HTMLElement.prototype usw.
Prototypische Delegation
Wie erzeuge ich ein Objekt, das an ein anderes delegiert?
- Funktion, deren prototype-Eigenschaft und der new-Operator (ES3)
-
Object.create() (ES5)
Prototypen
Wie bekomme ich den Prototyp eines Objekts?
-
obj.constructor.prototype (ECMAScript 3)
-
Object.getPrototypeOf(obj) (ES5)
-
obj.__proto__ (ES6, bereits viele Browser)
In der ECMAScript-Spezifikation [[Prototype]] genannt.
Prototypische Delegation
var o1 = { name: 'o1' };
// Erzeuge ein leeres Objekt, das o1 als Prototyp hat
var o2 = Object.create(o1);
// o2 hat keine Eigenschaft »name«, delegiert an o1
alert(o2.name); // o1
alert(o2.hasOwnProperty("name")); // false
o2.name = "o2";
// o2 hat nun eine Eigenschaft »name«
alert(o2.name); // o2
alert(o2.hasOwnProperty("name")); // true
Prototypenkette
Ein Objekt hat einen Prototyp. Der Prototyp hat einen Prototyp usw. bis zum obersten Prototyp Object.prototype.
var o1 = { name: 'o1' };
var o2 = Object.create(o1);
o2.__proto__ === o1
o1.__proto__ === Object.prototype
o2 → o1 → Object.prototype
var str = "Hallo Welt!";
str.__proto__ === String.prototype
String.prototype.__proto__ === Object.prototype
str → String.prototype → Object.prototype
Prototypen
Die Kern-Prototypen sind erweiterbar:
Array.prototype.max = function () {
var i, l = this.length, biggest = 0, candidate;
for (i = 0; i < l; i++) {
candidate = this[i];
if (candidate > biggest) {
biggest = candidate;
}
}
return biggest;
};
alert([6, 2, 9, 3].max());
Konflikte sind vorprogrammiert
Pseudoklassen
- JavaScript hat keine Unterscheidung zwischen festen Typen und Exemplaren
- JavaScript kennt nur einfache, erweiterbare Objekte, die delegieren können
- Prototypische Delegation ist vielseitiger
- In der Anwendungsentwicklung wird mit Pseudoklassen gearbeitet
- Einfachvererbung, Konstruktoren, Destruktoren, Super-Calls
Pseudoklassen
Konstruktoren, Prototypen, Instanzen
var Person = function (name) {
this.name = name;
};
Person.prototype.greet = function () {
alert("Hello, my name is " + this.name + "!");
};
var alice = new Person("Alice");
alice.greet();
alice → Person.prototype → Object.prototype
Konstruktoren
var Person = function (name) {
this.name = name;
};
- Konstruktoren sind normale Funktionen
- Über Konstruktoren lassen sich Typen definieren
- Konstruktoren sind Objektfabriken, sie erzeugen gleichförmige Objekte
Konstruktoren
var Person = function (name) {
this.name = name;
};
Konvention: Konstruktoren beginnen mit einem Großbuchstaben, um sie von normalen Funktionen zu unterscheiden
Konstruktor.prototype
Person.prototype.greet = function () {
alert("Hello, my name is " + this.name + "!");
};
- Jede Funktion hat eine Eigenschaft prototype
- Das ist ein einfaches, erweiterbares Objekt
- Definition von Methoden und Eigenschaften
Instanzen erzeugen
var alice = new Person("Alice");
alice.greet();
- Ein Aufruf des Konstruktors mit dem new-Operator erzeugt eine Instanz
- Erzeugt ein neues Objekt, das Konstruktor.prototype als Prototyp hat
- alice → Person.prototype
-
alice.greet kommt vom Prototypen
Schlüsselwort this
-
this hat viele Bedeutungen
- Außerhalb von Funktionen: window
- Innerhalb von Funktionen: Hängt von der Aufrufweise der Funktion ab.
var f = function () { alert(this); };
var o = { f: f };
f(); // window bzw. undefined im Strict-Mode
o.f(); // o
- Der Strict-Mode sorgt dafür, dass this nicht plötzlich window ist.
Programmieraufgabe
Schreiben Sie eine Klasse Robot, die die Methoden stepForward, turnLeft, turnRight und showLog implementiert. Die ersten drei speichern die Bewegung intern und geben eine Bestätigung aus. Die Methode showLog gibt auf der Konsole eine Liste aller aufgezeichneten Bewegungen aus. (Keine Visualisierung.)
Tipps
- Konstruktor Robot
- Robot.prototype.methode = function () {…};
- Eigenschaft this.log = [];
- Methoden fügen Aktionen zum Log hinzu: this.log.push('action');
- Ausgabe mit console.log(this.log.join(', '));
Beispiellösung
Pseudoklassen
- Warum ist das so umständlich mit Konstruktoren, prototype und new?
-
The pseudoclassical form can provide comfort to programmers who are unfamiliar with JavaScript, but it also hides the true nature of the language.– Douglas Crockford: JavaScript. The Good Parts
- Verzicht auf Konstruktoren zugunsten von Object.create()
Funktionale Programmierung
- Funktionen als »first-class citizens«
- Vollwertige Objekte, zur Laufzeit erzeugen
- Higher-order functions: Funktionen können Parameter und Rückgabewerte sein
- Anonym oder benannt, Auto-Referenzialität für Rekursion
- Closures
- Verschachtelung und »Callback Hell«
Funktionale Programmierung
var plusTwo = function (addend) {
return addend + 2;
};
var createMultiplier = function (f1) {
return function (f2) {
return f1 * f2;
};
};
var timesTwo = createMultiplier(2);
var compose = function (f1, f2) {
return function (arg) {
return f1(f2(arg));
};
};
var plusTwoTimesTwo = compose(timesTwo, plusTwo);
alert(plusTwoTimesTwo(2)); // 8
Zusammenfassung
- Objekte zur Programmstrukturierung
- Funktionen und Closures für private Daten
- Funktionale Programmierung
- Prototypen für Code-Wiederverwendung und Pseudoklassen
Grundpfeiler
JavaScript-Anwendungen
- Komfortable, leistungsfähige Anwendungen im Browser
- Gewohnte Interfaces, bessere Bedienbarkeit
- Synchronisierung mit dem Server im Hintergrund
- Wenige oder keine Seitenwechsel: Single-Page-Apps
- JavaScript lädt Daten und rendert das HTML
-
moviepilot.com, m.falk.de, salon.io
Anwendungsentwicklung
- Entwurfsmuster (design patterns)
- Wartbarkeit und Skalierbarkeit
- Performance und Memory-Management
- Browser-Kompatibilität
- Unit Testing, TDD
- Entwicklungsumgebungen
JavaScript-Anwendungen
- Anwendungsstruktur
- OOP und funktionale Programmierung
- DOM Scripting, HTML-Templates
- Routing und History
- Modularisierung und Dependencies
- API-Kommunikation
- Building und Packaging
Model View Controller
- Bewährtes Pattern für grafische Benutzeroberflächen
- Model: Rohdaten und deren Logik
- View: Darstellung der Daten, User Interface
- Controller: Benutzeraktionen auswerten, Daten manipulieren
- Controller erzeugt Model und View, View überwacht das Model
- MVC im Kontext von JavaScript verstehen
- Einfache und kleine Bibliothek (1.650 Zeilen)
- Bekannt und erprobt
- Aktive Entwicklung
- Lesbarer, getesteter Code
- Kostenlos und Open Source
- Ähnlich: Spine, CanJS
Backbones Abhängigkeiten
- Underscore, Lodash…
- Werkzeugkasten für funktionale und OOP
- jQuery, Zepto, Ender…
- für DOM Scripting und Ajax
- _.template, Mustache, Handlebars…
Backbones Grundideen
- Die Trennung von Models und Views(Separation of Concerns)
-
Models laden, verarbeiten und speichern die Daten
-
Views stellen Daten im DOM dar und erzeugen das User Interface
-
Router/History synchronisiert den Anwendungsstatus mit der URL
Backbone.Events
- Basis für eine Event-basierte Architektur
- Event-Handler registrieren und Events feuern
- Backbones Kernfeature, benutzt von allen anderen Klassen
- Komponenten kommunizieren über Events miteinander
- Methoden: on, off, trigger, listenTo
Models
- Abrufen, Verarbeiten und Speichern von Daten
- Models sind die »einzige Quelle der Wahrheit«
- Daten werden nicht im DOM gespeichert
- Kernfeature: Das attributes-Objekt
- Attribute lesen und schreiben mit get() und set()
- Änderungen erzeugen change-Events
Models
// Subclassing mit extend()
var Car = Backbone.Model.extend();
// Instanz mit vordefinierten Attributen
var car = new Car({
name: 'DeLorean DMC-12',
manufactured: 1981
});
// Attribut lesen
console.log( car.get('name') );
// Attribute schreiben
car.set('manufactured', 1982); // Ein Attribut
car.set({ manufactured: 1982 }); // Mehrere Attribute
console.log( car.get('manufactured') );
Demonstration
Models laden und speichern
- Synchronisierung über RESTful HTTP mit JSON
-
urlRoot angeben, z.B. cars
-
model.fetch() erzeugt ein GET /cars/:id
-
model.save() erzeugt ein POST/PUT /cars/:id
-
model.destroy() erzeugt ein DELETE /cars/:id
Models laden und speichern
var Car = Backbone.Model.extend({
urlRoot: '/cars'
});
model.fetch().then(successCallback, failCallback);
model.save().then(successCallback, failCallback);
model.destroy().then(successCallback, failCallback);
Promises / jQuery Deferreds
Model-Events überwachen
car.on('change', function (car, options) {
console.log('Some attribute changed');
});
car.on('change:manufactured', function (car, newValue, options) {
console.log('manufactured changed:', newValue);
});
car.set({ manufactured: 1982 });
Demonstration
Collections
- Eine Liste von Models
- Feuert die Events add, remove and reset
- Kurz gesagt, ein überwachbarer Array
- Listen-Helfer (each, map, reduce, sort, filter…)
Collections
var Car = Backbone.Model.extend();
var Cars = Backbone.Collection.extend({ model: Car });
var cars = new Cars([
{ name: 'DeLorean DMC-12', manufactured: 1981 },
{ name: 'Chevrolet Corvette', manufactured: 1953 }
]);
alert( cars.at(0).get('name') ); // DeLorean DMC-12
cars.push({ name: 'VW Scirocco', manufactured: 1974 });
alert( cars.length ); // 3
Views
- Eine View verwaltet ein Element (this.el, this.$el)
- Darstellung der Modeldaten (Render-Pattern)
- Referenz auf ein Model oder eine Collection
- Verarbeitet DOM-Events (Nutzereingaben)
- Überwacht Model-Events (Model-View-Binding)
- Ruft Model-Methoden auf oder emittiert Events
Einfache View ohne Template
var CarView = Backbone.View.extend({
initialize: function () {
// Überwache Model-Änderungen: Neu rendern
this.listenTo(this.model, 'change', this.render);
},
render: function () {
this.$el.html('Name: ' + this.model.get('name'));
}
});
var carView = new CarView({
model: car,
el: $('#car')
});
carView.render();
Demonstration
Templates
Views übersetzen Modeldaten in HTML mithilfe eines Templates
Modeldaten:
{ message: 'Hello World' }
Template:
<p>{{message}}</p>
Ausgabe:
<p>Hello World</p>
Der erzeugte HTML-Code wird ins DOM eingefügt
View mit Template
var CarView = Backbone.View.extend({
template: _.template('Name: {{name}}'),
initialize: function () {
this.listenTo(this.model, 'change', this.render);
},
render: function () {
this.$el.html(this.template(this.model.attributes));
}
});
var carView = new CarView({
model: car,
el: $('#car')
});
carView.render();
Demonstration
Model-View-Binding
- Muss bei Backbone manuell eingerichtet werden
- Eine View hört auf Model-Änderungen und rendert sich neu oder aktualisiert das DOM
- this.listenTo(this.model, 'change', this.changeHandler)
- Eine View hört auf Nutzereingaben und ruft Model-Methoden auf oder feuert Events beim Model
Demonstration
Grenzen von Backbone
Backbone.js gives structure … by providing models with key-value binding and custom events, collections with a rich API of enumerable functions, views with declarative event handling, and connects it all to your existing API over a RESTful JSON interface. – backbonejs.org
Das ist alles (plus Routing).
Nachteile von Backbone
- Mit Absicht sehr minimalistisch und offen
- Überlässt einem viele Entscheidungen, bietet wenig Orientierung
- Keine Patterns zur Strukturierung der Anwendung auf oberster Ebene
- Kein klassisches MVC, MVP oder MVVM
- »There’s More Than One Way To Do It« vs. »Convention Over Configuration«
- Backbone alleine reicht nicht, ist aber eine gute Grundlage
Application Frameworks
Auf Basis von Backbone
Zusammenfassung
- Backbone als MV*-Framework
- var Car = Backbone.Model.extend({…});
- var CarView = Backbone.View.extend({…});
- Model-View-Binding
- Rendering und Templates
- Backbone bietet keine übergreifende Struktur
- Anwendungsarchitektur auf Basis von Backbone
- Best Practices und Konventionen
- In CoffeeScript und AMD geschrieben
- Entstanden aus moviepilot.com
- Open Source, automatisiert getestet
Chaplin-Komponenten (1)
-
Application startet die Kernkomponenten
-
Router überwacht URL-Änderungen und prüft, ob Routen auf die URL passen
-
Dispatcher startet und verwaltet Controller wenn Routen passen
-
Controller erzeugen Models und Views
Chaplin-Komponenten (2)
-
Composer zum Wiederverwenden von Models/Views zwischen Controllern
-
Layout ist die oberste View, fängt Klicks auf Links ab
-
Regions sind benannte Bereiche im UI, die gefüllt werden können (z.B. main-content, sidebar)
-
mediator zur Kommunikation via Publish/Subscribe
Routing-Ablauf
Router ⇢ Dispatcher → Controller → Model/View
⇢
benachrichtigt
→
erzeugt
Routen
- Konfigurationsdatei routes.js
- ähnelt routes.rb in Rails
match('', 'homepage#show');
match('cars', 'cars#index');
match('cars/:id', 'cars#show');
- Pfad / – Controller homepage – Action show
- Pfad /cars – Controller cars – Action index
- Pfad /cars/:id – Controller cars – Action show
Controller
var CarsController = Controller.extend({
// cars/1
// cars/:id
show: function (params) {
this.car = new Car({ id: params.id });
this.view = new CarView({ model: this.car });
}
});
Controller
- Erzeugen Models/Collections (this.model/this.collection)
- Erzeugen die Haupt-View (this.view)
- Methoden sind Actions
- Nehmen URL-Parameter entgegen
Controller
- Ein aktiven Controller, der die Haupt-View erzeugt
- Beim Starten eines Controllers wird der vorherige Controller mitsamt Models und Views abgebaut
- Models und Views können kontrolliert wiederverwendet werden (Composer)
- Persistente Controller sind möglich
Object Disposal
- Definierter Lebenszyklus von Controllern, Models und Views
- Alle Komponenten haben eine dispose-Methode als Destructor
- Garbage Collection ermöglichen zur Vermeidung von Memory-Leaks
- Wichtig bei Event-basierten Architekturen
- Chaplin wirft per default alles weg
Mediator
- MVC-Komponenten haben keine Referenzen aufeinander
- Ein drittes Objekt zum Datenaustausch
- Publish/Subscribe-Pattern
-
mediator.publish(), mediator.subscribe()
- In Models, Collections und Views: this.subscribeEvent(), this.publishEvent()
- Zentrale geteilte Objekte: mediator.user
Modularisierung
- Chaplin nutzt die Modulstandards CommonJS bzw. AMD
- Abhängigkeiten zwischen Klassen deklarieren
- Abhängigkeitsbaum maschinell auslesen
- Richtige Ladereihenfolge der Scripte
- Lazy-Loading von Abhängigkeiten
- Packaging mehrerer Module in einer Datei
AMD: require()
Module asynchron laden und dann Code ausführen
require(dependencies:Array, callback:Function)
require(['application'], function(Application) {
var app = new Application();
app.initialize();
});
AMD: define()
Module mit Abhängigkeiten definieren
define(name:String, dependencies:Array, callback:Function)
define(dependencies:Array, callback:Function) // Name implizit
controllers/hello_world_controller.js
define(
[
'controllers/base/controller',
'models/hello_world',
'views/hello_world_view'
],
function (Controller, HelloWorld, HelloWorldView) {
'use strict';
var HelloWorldController = Controller.extend({ … });
return HelloWorldController;
}
);
AMD-Modulkonventionen
- Chaplin besteht aus Modulen
- Jede Klasse oder Singleton-Objekt ist ein Modul
- Eine Klasse pro Datei, ein define() pro Datei
- Der Name ist implizit und entspricht dem Dateipfad ohne Endung,z.B. controllers/hello_world_controller
Chaplin-Views
- Vordefinierte render-Methode
- Deklaratives Event-Handling:events und listen
- Optionen: autoRender und container
- Subviews mit der subview-Methode
Beispielanwendung mit Chaplin
- github.com/molily/chaplin-cars
- Create, Read, Update, Delete (CRUD)
- Listenansicht, Einzelansicht, Editieransicht
- Hinzufügen von neuen Einträgen
- Vorgegebene Daten, kein Server, Persistenz mit localStorage