HTML5 web applications frequently need to attach a view class to existing HTML. For example, the server-side piece of the application often renders a navigation bar and some other basic page elements. Some applications even render the first page's content so the Marionette application won't have to make a separate request for its initial content.
In this situation we'll usually want to attach a Marionette or plain Backbone view class to some of the existing HTML. The view is a convenient high-level mechanism for handling events and performing other UI-related tasks. That's enough of a reason to create a view even if we don't need the view to render new HTML. A navigation bar view class, for example, might handle click events, dynamically update the navbar's menu items, and maybe set up a handler for a search box.
With Backbone & Marionette, it's easy to accomplish this.
There are actually a couple of ways to do it, depending on the situation. This current post covers the first way, and I'll discuss the second option in a subsequent post.
Using a simple view by itself
The first case is when we won't need to replace the existing HTML fragment that our view takes over. This is often the right approach for a navigation bar: the HTML structure doesn't change, but we do want to add some event handlers and other logic.
The key piece of this solution is to use the el
option to pass the existing element's selector to the new view's constructor. This is a Backbone mechanism, and it works for both Backbone & Marionette views:
The Backbone view's constructor will still set up event handlers specified in the view's events
map, if any. In fact, this automatic event handling is usually the main reason I want to use a view in the first place; it's a terrific convenience.
A navbar example
Let's hook up a view to a navbar. Here's how it will look in the browser:
Here is the markup, which uses Bootstrap's navbar mechanism:
[object Object]Right now, clicking the navbar links doesn't change the active link -- for that, we need some JavaScript code to set the active
CSS class on the selected element only. This is why we want to attach a view class to this existing markup: a view is the perfect place for that JavaScript, and it will let us interact with the page using the same Marionette constructs like View and Controller that we use throughout the rest of the application.
We'll start by attaching a minimal view. This code uses a Marionette Controller class for convenience. The code comes from the working example code mentioned at the end of this post. In that example code, the startup code calls the controller's start
function when the application is ready to run, and that's where we create the view instance and pass it the #navbar-links
selector to connect to the existing HTML navbar fragment.
That connects our view to the #navbar-links
element, but the view doesn't actually do anything yet -- there is no functionality at this point. So now let's put it to work.
Adding event listeners
We want our view to listen for click events in the links' a
tags, and change the active
CSS class so that clicking a navbar link will visually toggle the state of the navbar buttons.
We'll use Backbone's events
map to add a listener for the click
event. Here's the new version of our View class:
The events
map specifies a handler for click
events coming from any a
tags. For Backbone & Marionette views, these event are only within the scope of the view's root element, so we won't be affected by any a
tags outside this narrow section of the navbar.
The click handler function onClickLink
first toggles the active li
element into an unselected state by removing the active
CSS class. Then the next statement blurs the newly-selected link and marks its parent li
element as active
so it will appear with Bootstrap's visual decoration for the selected link. (The blur step removes the annoying dotted-rectangle marker that FireFox shows for selected a
tags.)
That's all it takes; now we have a functioning navbar in our small Marionette application!
Optionally binding a ui
map
In my production code, I usually add one more thing to my render
functions that attach themselves to existing HTML:
Instead of an empty render
, I add a call to the view's bindUIElements
function. Marionette views support a ui
map that binds view variables to UI elements for us, saving us the trouble of using jQuery ourselves to access them. This is a handy shortcut for elements we need to work with frequently. The view's built-in bindUIElements
function does this for us, and it is called in Marionette's default render
function. Since we're overriding render
ourselves, we have to call bindUIElements
manually if we want it. More often than not, I end up using the ui
map in my navbar views & other views that attach to existing HTML, so I've gotten in the habit of always adding this call to bindUIElements
. Doing this saves me -- or another programmer who comes after me -- from going through a frustrating debugging session when I later add a ui
map to this view, only to find it doesn't work because I've overridden render
and the default call to bindUIElements
isn't being made anymore. It's safe to add the call to bindUIElements
even if I'm not using a ui
map; the call is essentially a no-op if my view doesn't have a ui
map, so it's a cheap insurance.
I encourage you to check out the runnable example on github. I've frequently made use of this construct in Backbone & Marionette projects, as well as another method of attaching a view to existing HTML. I'll cover the second way next time in part two of this post.