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]
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:
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
That's all it takes; now we have a functioning navbar in our small Marionette application!
Optionally binding a
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.