I'm trying to create an application similar to a floor plan editor in Backbone.js and Raphael. I'm not sure if what I'm doing is structured correctly or not.
I'm not using the Backbone routing, just the basic Models/Collections/Views.
My views are simple enough. A Building contains a collection of floors, and a floor contains a collection of rooms. The building model is populated by a JSON feed.
Building: Backbone.Model.extend({
initialize: function() {
this.set({
floors: new Floors(this.get("floors"))
});
}
});
Floor: Backbone.Model.extend({
initialize: function() {
this.set({
rooms: new Rooms(this.get("rooms"))
});
}
});
Room: Backbone.Model.extend({
initialize: function() {
// This is what contains the Raphael attributes that is necessary
// to render the object. pathString, transformString, fill and stroke colors, etc.
},
render: function() { }
});
Now from all the examples I've seen, a Backbone view is supposed to be tied to an element. However since I'm using Raphael and require additional objects (when rendering a FloorView I also render an edit and delete button, labels for the name and dimensions, etc.), I bind a view to a Raphael set instead.
BuildingView : Backbone.View.extend({
layout: null,
initialize: function() {
this.el = Raphael("building", 600, 600);
this.layout = this.el.set();
// Backbone Event Handler for all views
this.eventAggregator.on("floorRemoved", this.removeFloor, this);
},
removeFloor: function(floor) {
// Remove the nested Raphael set
this.layout.splice($.inArray(floorView.el, this.layout), 1);
// Remove from the model
this.model.get("floors").remove(floorView.model);
}
});
FloorView : Backbone.View.extend({
initialize: function() {
// Pass in the paper object from the BuildingView initialize method
this.el = this.options.paper.set();
var self = this;
this.model.get("rooms").each(function(room) {
var rv = new RoomView({
model: room,
paper: this.options.paper
});
var self = this;
// delete object gets added here
// ...
deleteObj.click(function() {
self.eventAggregator.trigger("floorRemoved", self);
self.el.remove();
});
}
});
This setup seems to work fine, but I am running into some issues on how to handle removing a floor. So the click function on the delete floor method will trigger an event to let the building know to remove it from the parent Raphael set and the floors model collection, this seems to clean up the Raphael/model side nicely. I'm not sure what to do when it comes to the views though.
I need to do two things:
- Properly remove the view that was deleted, as well as the child views
- Reposition/re-render all the remaining views
Do I need my BuildingView and FloorView to keep track of the child views so that it can loop through to call a render method on them? Then my BuildingView would have two collections, one for the views and one for the models. Is this the right way to go about handling nested views?