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
  • Ursprü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 3: 1999
    • Edition 5.1: 2011
    • Edition 6: 2015
  • 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
  • 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, Nashorn)

Standards

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)

<code contenteditablevar 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 </code></pre>

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

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

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 </a>

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 </code></pre>

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

OOP-Werkzeuge

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> </table> Der erzeugte HTML-Code wird ins DOM eingefügt </code></pre>

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

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 </tr>
erzeugt </tr> </table>

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

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

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