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
# 9elements - 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 1. Sprachgrundlagen 2. JavaScript in HTML, jQuery 3. Strukturierung: OOP und funktionale Programmierung 4. 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)
# Standards - ECMAScript (es5.github.com) - W3C (w3.org) - HTML WG, WebApps WG usw. - Community Groups - WHATWG (whatwg.org) - HTML, DOM usw. - Übersicht
# 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… 1. Statements (u.a. Kontrollstrukturen) 2. Expressions (Ausdrücke) 3. 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()`
# Referenzen - Mozilla Developer Network (MDN) - Microsoft Developer Network (MSDN) - SELFHTML (lückenhaft, aber Grundlagen)
# 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);
# jQuery (jquery.com) - 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 1. GET-Request auf die JSON-API von Flickr - `ajax()` oder `getJSON()` 2. HTML für Ergebnisse zusammenbauen und ins DOM einfügen - `html()` oder `append()` 3. Vollansicht bei Klick - `html()`, `click()`
# Helferlein - api.jquery.com - jqapi.ru - oscarotero.com/jquery/ - developer.mozilla.org - Debugger und Konsolen von Firebug und Web Inspector - Debugging mit `alert('…');` und `console.log('…');`
# 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 verstehen JavaScript-Dokumentation
# 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
# Prototypenkette Siehe Web Inspector
# 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()
# Literatur - Organisation von JavaScripten - Konstruktoren, Prototypen und Instanzen - Ebenda: Objektverfügbarkeit und this-Kontext - Die Grundlagen von JavaScript (dt. Übersetzung)
# OOP-Werkzeuge - Klassenhierarchien, Mehrfachvererbung, Mixins, Traits, AOP - Helfer für simple Vererbung - Backbones extend() - Dojo / compose - PrototypeJS / Mootools - Yahoo YUI - Traits / Method combinators - JSClass
# 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
# Backbone.js (backbonejs.org) - 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… - für HTML-Templates
# 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-Übersicht
# 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 - Marionette - Chaplin - Thorax - Aura
# 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
# Chaplin.js (chaplinjs.org) - Anwendungsarchitektur auf Basis von Backbone - Best Practices und Konventionen - In CoffeeScript und AMD geschrieben - Entstanden aus moviepilot.com - Open Source, automatisiert getestet
# Chaplin.js
# 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`
# AMD-Bibliotheken - Require.js – browserseitiger AMD-Loader - curl.js – Alternative - r.js – Pakete aus AMD-Modulen schnüren - Almond.js – browserseitige Minimalimplementierung ohne Loader - Browserify – Pakete aus CommonJS-Modulen schnüren
# AMD-Beispiel Demonstration
# Chaplin-Views - Vordefinierte `render`-Methode - Deklaratives Event-Handling:
`events` und `listen` - Optionen: `autoRender` und `container` - Subviews mit der `subview`-Methode
# Chaplin-Boilerplate - github.com/chaplinjs/chaplin-boilerplate-plain - Hello-World-Beispiel - Application, Routes, Controller, Model, View - Handlebars.js als Templating-Engine
# 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`