Interaktive Ansicht

Submitted by Erik Wegner on

Jetzt wird die Backbone.js-Ansicht aus der Einführung so erweitert, dass die typischen Aktionen Hinzufügen, Bearbeiten und Löschen möglich sind.

tl;dr

Es kommt die Ereignisbehandlung im Browser/Frontend/GUI hinzu. Direkt ausprobieren geht unter JSFIDDLE Demo.

Anpassungen und Vorbereitung

  1. Zur Sicherheit lege ich die Kodierung auf UTF-8 fest (Zeile 4).
  2. Die einfachen Liste ändere ich ab in eine Tabelle. Dazu werden die jeweiligen tagName-Eigenschaften angepasst (Zeile 13 und 25).
  3. Damit der View eines Elements als Tabellenzeileninhalt funktioniert, passe ich das Template an (Zeile 27).
  4. Auf der Seite wird noch ein Aufruf zum Hinzufügen platziert (Zeile 22).
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Backbone Tutorial</title>
<script src="/jquery-1.11.0.min.js" type="text/javascript"></script>
<script src="/underscore-min.js" type="text/javascript"></script><!-- Version 1.6.0 -->
<script src="/backbone-min.js" type="text/javascript"></script><!-- Version 1.1.1 -->
</head>
<body>
<script type="text/javascript">
var MasterView = Backbone.View.extend({
	tagName: "table",
	className: "masterview",
	addOne: function(element) {
		var itemview = new ItemView({ data: element });
		this.$el.append(itemview.render().el);
	}
});

var masterview = new MasterView();
jQuery('body').append(masterview.$el).append('<a href="javascript:bbadd();">+</a>');

var ItemView = Backbone.View.extend({
	tagName: "tr",
	className: "backboneitem",
	template: _.template('<td><span class="show"><%= title %></span></td>'),
	
	initialize: function(options) {
	    if (options.data) {
			this.data = options.data;
			this.listenTo(this.data, "remove", this.remove);
			this.listenTo(this.data, "change:title", this.render);
		}
	},
	
	render: function() {
		this.$el.html(this.template(this.data.toJSON()));
		return this;
	}
});

var Items = Backbone.Collection.extend();
var items = new Items();

masterview.listenTo(items, "add", masterview.addOne);

items.add({ id: 17, title: 'Apfel' });
items.add({ id: 23, title: 'Birne' });
items.add({ id: 42, title: 'Orange' });
</script>
</body>
</html>

Alle Anpassungen sind in der Datei file1000.html festgehalten

Hinzufügen

Um Elemente zu ergänzen, soll beim Klick auf den Link der Titel des neuen Elements abgefragt werden. Anschließend wird das Element erzeugt und der Collection hinzugefügt. Die folgende Funktion ist dafür ausreichend:

var bbadd = function() {
    var title = prompt("Neuer Titel?");
    if (title != "" && title != null) {
        items.add({title: title});
    }
}

Der Zustand ist in der Datei file1010.html festgehalten.

Löschen

Um Elemente aus der Collection zu löschen, fügen wir dem ItemView einen Bereich hinzu, der unsere Löschfunktion auslöst. Dazu schreiben wir als erstes etwas CSS in das HTML-Grundgerüst:

.delete {
 cursor: pointer;
}

Danach tausche ich das Template des ItemView gegen folgenden Text aus. Damit erreiche ich, dass in jeder Zeile eine weitere Zelle entsteht, die den Löschlink enthält. Der »Link« ist ein einfaches SPAN-Element, das für sich genommen noch keine Funktion hat. Selbstverständlich wäre auch die Nutzung eines A-Elements möglich.

template: _.template('<td><span class="show"><%= title %></span></td><td><span class="delete">✗</span></td>'),

Damit nun beim Klick auch eine Funktion ausgelöst wird, kommt ein Sonderfall der Events zum Einsatz. Bei der Definition eines Views ist es möglich, direkt die zu verarbeitenden Ereignisse anzugeben. Dies ist in folgendem Code in den Zeilen 6 bis 8 dargestellt. Die ereignisverarbeitende Funktion (ab Zeile 14) wird als Zeichenkette hinter dem jQuery-Selektor angegeben (in Zeile 7).

var ItemView = Backbone.View.extend({
	tagName: "tr",
	className: "backboneitem",
	template: _.template( "" /* gekürzt */ ),
	
	events: {
	   "click .delete": "clickOnDelete"
	},
	
	initialize: function(options) { /* gekürzt */ },
	
	render: function() { /* gekürzt */ },
	
	clickOnDelete: function() {
	  if (confirm(this.data.get("title") + " löschen?")) {
	     this.data.collection.remove(this.data);
	  }
	}
});

In der Ereignisverarbeitung (Zeile 14 – 18) wird eine Sicherheitsabfrage ausgelöst.

Sicherheitsabfrage beim Löschen

Bestätigt der Benutzer den Dialog, wird das Element gelöscht. Weil die Collection nur im Browser vorhanden ist, reicht es aus, das Element aus der Collection zu entfernen.

Der Zustand ist in der Datei file1020.html festgehalten.

Bearbeiten

Die letzte Aufgabe besteht darin, die vorhandenen Elemente bearbeiten zu können. Die Umsetzung erfolgt analog zum Löschen:

  1. Anpassung der CSS-Anweisung:
    .edit,
    .delete {
     cursor: pointer;
    }
  2. Erweiterung des Templates um eine weitere Zelle:
    template: _.template('<td><span class="edit">✏</span></td><td><span class="show"><%= title %></span></td><td><span class="delete">✗</span></td>'),
  3. Ergänzung der Zeile 38 in den events des ItemView:
    	events: {
    	   "click .delete": "clickOnDelete",
    	   "click .edit"  : "clickOnEdit"
    	},
  4. Ergänzung der Funktion zur Ereignisbehandlung:
    clickOnEdit: function() {
    	  var newtitle = prompt("Neuer Titel", this.data.get("title"));
    	  if (newtitle != null && newtitle != "") {
    	    this.data.set("title", newtitle);
    	  }
    	}

Der Zustand ist in der Datei file1030.html festgehalten.

Zusammenfassung

Der Quellcode der Seite sieht nun so aus:

<!DOCTYPE html>
<html>
<head>
<!-- http://jsfiddle.net/ErikWegner/hnLkk/ -->
<meta charset="utf-8">
<title>Backbone Tutorial</title>
<script src="/jquery-1.11.0.min.js" type="text/javascript"></script>
<script src="/underscore-min.js" type="text/javascript"></script><!-- Version 1.6.0 -->
<script src="/backbone-min.js" type="text/javascript"></script><!-- Version 1.1.1 -->
<style type="text/css">
.edit,
.delete {
 cursor: pointer;
}
</style>
</head>
<body>
<script type="text/javascript">
var MasterView = Backbone.View.extend({
	tagName: "table",
	className: "masterview",
	addOne: function(element) {
		var itemview = new ItemView({ data: element });
		this.$el.append(itemview.render().el);
	}
});

var masterview = new MasterView();
jQuery('body').append(masterview.$el).append('<a href="javascript:bbadd();">+</a>');

var ItemView = Backbone.View.extend({
	tagName: "tr",
	className: "backboneitem",
	template: _.template('<td><span class="edit">✏</span></td><td><span class="show"><%= title %></span></td><td><span class="delete">✗</span></td>'),
	
	events: {
	   "click .delete": "clickOnDelete",
	   "click .edit"  : "clickOnEdit"
	},
	
	initialize: function(options) {
	    if (options.data) {
			this.data = options.data;
			this.listenTo(this.data, "remove", this.remove);
			this.listenTo(this.data, "change:title", this.render);
		}
	},
	
	render: function() {
		this.$el.html(this.template(this.data.toJSON()));
		return this;
	},
	
	clickOnDelete: function() {
	  if (confirm(this.data.get("title") + " löschen?")) {
	     this.data.collection.remove(this.data);
	  }
	},
	
	clickOnEdit: function() {
	  var newtitle = prompt("Neuer Titel", this.data.get("title"));
	  if (newtitle != null && newtitle != "") {
	    this.data.set("title", newtitle);
	  }
	}
});

var Items = Backbone.Collection.extend();
var items = new Items();

masterview.listenTo(items, "add", masterview.addOne);

items.add({ id: 17, title: 'Apfel' });
items.add({ id: 23, title: 'Birne' });
items.add({ id: 42, title: 'Orange' });

var bbadd = function() {
    var title = prompt("Neuer Titel?");
    if (title != "" && title != null) {
        items.add({title: title});
    }
}
</script>
</body>
</html>

Darin implementiert sind die typischen CRUD-Operationen: der Benutzer kann im Browser zur Laufzeit dynamisch Elemente anzeigen lassen, ergänzen, bearbeiten und löschen.

Als neue Technik kommt hier die Ereignisbehandlung in den Backbone.Views zum Einsatz.

Der gesamte Code kann auch unter JSFIDDLE Demo direkt im Browser ausprobiert werden.