Model View Controller mit Backbone.js

Seite 4: Routen, Ansichten

Inhaltsverzeichnis

Wie bereits erwähnt, kümmert sich Backbone.js an sich nicht um das Aktualisieren der Darstellung im Webbrowser, sondern überlässt diese Aufgabe dem Entwickler und einer von ihm gewählten Bibliothek. Dennoch stellt Backbone.js die Basisklasse Backbone.View zur Verfügung, die zur Definition von Ansichten dient. Eine Ansicht entspricht allerdings eher dem aus MVC bekannten Controller, übernimmt also die Kommunikation zwischen der Webseite und dem Domänenmodell. Zum einen reagiert eine Ansicht auf Eingaben im Webbrowser, indem sie Funktionen am Domänenmodell aufruft. Zum anderen veranlasst sie die Aktualisierung der Darstellung im Webbrowser, wenn Änderungen am Domänenmodell auftreten.

Um eine Ansicht zu definieren, muss man sie analog zu Entitäten und Listen von der Klasse Backbone.View ableiten und einige Eigenschaften definieren: Mindestens tagName ist einzubauen, da die Eigenschaft definiert, als welches HTML-Element die Ansicht gerendert werden soll. Zusätzlich kann man die Eigenschaften className und id angeben, um eine CSS-Klasse und -ID festzulegen:

var CustomerView = Backbone.View.extend({
tagName: 'div',
className: 'customerDetail'
});

Außerdem ist es sinnvoll, die initialize-Funktion zu definieren und in ihr als Reaktion auf eine Änderung des Domänenmodells die render-Funktion aufzurufen. Die Aufgabe dieser Funktion besteht darin, die Darstellung im Webbrowser zu aktualisieren. Wie sie implementiert wird, überlässt Backbone.js jedoch vollständig der Fantasie des Entwicklers:

var CustomerView = Backbone.View.extend({
tagName: 'div',
className: 'customerDetail',
initialize: function () {
this.listenTo(this.model, 'change', this.render);
},
render: function () {
// ...
}
});

In der Regel übernimmt die render-Funktion selbst nicht das eigentliche Aktualisieren, sondern erzeugt lediglich den erforderlichen HTML-Code, um die Ansicht anzuzeigen. Das weist sie der Eigenschaft el beziehungsweise deren jQuery-gekapselter Variante $el zu, die das konkrete HTML-Element repräsentiert, das mit der Ansicht verknüpft wurde. An dieser Stelle bindet man üblicherweise auch die Verwendung von HTML-Vorlagen ein, wobei sich im einfachsten Fall wiederum auf die template-Funktion von Underscore.js zurückgreifen lässt.

Damit man die render-Funktion an übergeordneter Stelle verkettet aufrufen kann, empfiehlt die Dokumentation von Backbone.js, dass die Funktion this an den Aufrufer zurückgibt:

var CustomerView = Backbone.View.extend({
template: _.template(...),
tagName: 'div',
className: 'customerDetail',
initialize: function () {
this.listenTo(this.model, 'change', this.render);
},
render: function () {
this.$el.html(this.template(this.model.attributes));
return this;
}
});

Außerdem lässt sich durch die events-Eigenschaft auf Ereignisse innerhalb des Webbrowsers reagieren, um beispielsweise entsprechende Funktionen an der Ansicht aufzurufen:

var CustomerView = Backbone.View.extend({
template: _.template(...),
tagName: 'div',
className: 'customerDetail',
events: {
'click .save': 'saveCustomer'
},
initialize: function () {
this.listenTo(this.model, 'change', this.render);
},
render: function () {
this.$el.html(this.template(this.model.attributes));
return this;
},
saveCustomer: function () {
// ...
}
});

Beim Erzeugen der konkreten Ansicht kann man dem Konstruktor als Parameter ein bestehendes HTML-Element übergeben, an das sie gebunden wird. Außerdem kann man dem Konstruktor dynamisch die anzuzeigende Entität beziehungsweise die anzuzeigende Liste übergeben, indem man dem Parameterobjekt die Eigenschaft model beziehungsweise collection hinzufügt:

var customerView = new CustomerView({
el: document.getElementById(...),
model: customer
});

Auch für das Verwalten von Routen enthält Backbone.js Unterstützung. Basis hierfür ist die Klasse Backbone.Router, die sich auf dem üblichen Weg ableiten lassen. Als Parameter sind der extend-Funktion ein Objekt mit Routen und die von ihnen aufzurufenden Funktionen zu übergeben:

var AppRouter = Backbone.Router.extend({
routes: {
'customers/:id': 'loadCustomer',
'customers': 'loadCustomers'
},
loadCustomer: function (id) {
// ...
},
loadCustomers: function () {
// ...
}
});

Um einen konkreten Router zu erzeugen, muss man AppRouter als Konstruktor aufrufen:

var appRouter = new AppRouter();

Die Existenz einer Router-Instanz an sich genügt jedoch noch nicht, damit Backbone.js die Navigation des Webbrowsers verwaltet. Darüber hinaus muss man noch die start-Funktion des Objekts Backbone.history aufrufen.

Dieser Funktion kann man dabei als Parameter übergeben, ob man Hashbang nutzende Routen oder die in HTML5 neu eingeführte, ausschließlich mit modernen Webbrowsern kompatible History-API verwenden möchte:

Backbone.history.start({ pushState: true });

Da die Navigation mit Hashbang im Internet Explorer die Verwendung eines iframe-Elements voraussetzt, ist es wichtig, die start-Funktion erst nach dem vollständigen Laden der Webseite aufzurufen. Aus diesem Grund ist man gut beraten, das Instanziieren der Router und den Aufruf der Funktion in einen Aufruf der $-Funktion von jQuery zu kapseln:

$(function () {
var appRouter = new AppRouter();
Backbone.history.start({ pushState: true });
});