A common use case is to swap between views in a common DOM element; this can be done by using the same el
property in both views and calling the render()
method on the view you want to see. But this way doesn't clean the memory and event bindings because both views will remain live in memory, even if they are not in the DOM.
A particularly useful scenario is when you need to switch between sub-applications, because sub-applications are rendered in the same DOM element normally. For example, when a user wants to edit contact information, he/she will click on an Edit button, and the current view will be replaced with an edit form.
To switch between views, a Region
class could be used as shown next:
var mainRegion = new Region({el: '#main'}); var contactViewer = new ContactViewer({model: contact}); contactViewer.on('edit:contact', function(contact) { var editContact = new EditContactView({ model: contact }); mainRegion.show(editContact); }); mainRegion.show(contactViewer);
The Region
object points to an existing DOM element; to show a view on that element, the show()
method should be called on the Region
object. Note that views don't have the el
property set because regions will put the element in the DOM and not the View itself. This gives us an extra feature, views don't need to set an el
property anymore and can be rendered on any available region.
A basic region manager can be implemented with this code:
class Region { constructor(options) { this.el = options.el; } // Closes any active view and render a new one show(view) { this.closeView(this.currentView); this.currentView = view; this.openView(view); } closeView(view) { // Only remove the view when the remove function // is available if (view && view.remove) { view.remove(); } } openView(view) { // Be sure that this.$el exists this.ensureEl(); // Render the view on the this.$el element view.render(); this.$el.html(view.el); } // Create the this.$el attribute if do not exists ensureEl() { if (this.$el) return; this.$el = $(this.el); } // Close the Region and any view on it remove() { this.closeView(this.currentView); } }
When the show()
method is called, it closes the current view, if any, then assigns a new currentView
and opens the view. When a view is open, Region
ensures that the $el
property exists, first calling the ensureEl()
method. Then the interesting part happens:
view.render(); this.$el.html(view.el);
The Backbone documentation explains how views works:
All views have a DOM element at all times (the el property), whether they've already been inserted into the page or not. In this fashion, views can be rendered at any time, and inserted into the DOM all at once [...]
And that's what happens here: we render the view in memory first, calling view.render()
, and then insert the result in the DOM pointed by the Region $el
property.
A remove()
method is implemented too, to make regions compatible with Backbone Views. When a region is removed, it needs to close the owned view too, so this allows us to do this easily.
Imagine that we have a region that owns a CollectionView
with many views inside; when the remove()
method is called on the region, it will call the remove()
method on the CollectionView
, which will call the remove()
method on every child view.
18.119.253.31