“Marionette Maps” is an on-going series on building a loosely coupled, configuration driven map viewer using Marionette.js. Read Part 1
TL;DR; Version
This covers Marionette Application, Module, Controller, ItemView, CollectionView and a little on Events.
Code: github tagged v0.0.2
Demo: http://dbouwman.github.com/geomacmapper
The Long Version…
Working from the rough layout I created in the last post, the next thing to do is dive into the javascript.
First, grab Marionette and it’s dependencies from MarionetteJs.com. Drop these into a /javascript/libs/marionette folder, and add script tags into our index.html to load them up.
Creating the Application
The next thing we need is a Marionette Application. This will form the hub of our application, and the main starting point that we pass in our configuration json that will setup the app for us.
At this point the application is super simple as shown in this gist…
As we develop the application we will setup more Regions – right now we just have the one region for the tools – and this is the container that will hold our tool items in the upper right. Unlike vanilla Backbone, with Marionette we don’t usually inject elements directly into the DOM – instead we use a Region, which helps mitigate issues with zombie event handlers, but more on this later.
The Navbar: Module + Controller
The top bar of our app has a few things to manage. From left to right, it’s got a logo, the “menu” label and icon, and then a set of tools off on the right.
The first three things can be in-lined into the page as they are pretty static, but we want to build our app to be configurable, and the tools sound like something we may want to change down the line. Since we’re working with javascript, we are going to pass in our configuration as json – here is the our starting “options” object:
Ok, now we need to write some code to manage the Navbar. In Marionette we create a Controller to handle this, and we put it inside a Module to keep things nice and tidy. In the gist you can see that we create the module by calling the module() method on our application. This is standard Marionette practice, and it allows the application to “know” about a module, and call initialize() on it as the app is spinning up. Here is an example of a skeleton of a Module with a Controller.
Now, to wire things up. First off, the Module will get passed the options hash that we pass into the application. In the Module’s initialize method, we create a controller, and pass the options hash in, along with the Region.
From there the process is pretty simple: we take the toolItems array from the options hash, convert that into a Backbone.Collection of ToolItemModels, then throw that to a ToolListView and ask the region to show that view. Then wire up events from the navbar, and respond to any Application events, and it’s done. Simple!
Breaking It Down
Backbone works with models and collections of models. So we need to setup our model by extending Backbone.Model. Same thing with the collection, but we extend… (wait for it) Backbone.Collection. This is at line 65 in the gist below.
Now we get to some Marionette goodness. To display our tool items, we want to iterate a list (the collection) and use that to create a ul > li structure. The individual li elements will be created and managed by instances of ToolItemView, which extends Marionette.ItemView. The ul and it’s contained collection of li’s (ToolItemView) will be created by an instance of ToolListView, which extends Marionette.CollectionView. This is at line 71 in the gist below. Since we want something to happen when a user clicks these tools, we need to have a handler in the mix. This is another place where Backbone & Marionette really rock – the events hash in the ToolItemView definition sets up the click event. When this is called, the model for that item will automagically be set to this.model. Since we are building a decoupled application, the implementation of these tools will be in another module, so all we do in the itemView is trigger an event – the name of which is specified in our options hash. Neat, clean, and oh so separated!
In proof reading this, it sounds awfully complex, but it’s really pretty straight forward. Granted that for this particular item, this may be overkill, but as your app grows, doing it all via a consistent set of patterns will massively out-weight the little extra code.
While working on this I decided to swap out the images for icon fonts, and a simple jsfiddle - check it out as it’s a nice place to play with ItemViews and CollectionViews outside of a module/controller/application environment. It also shows that you can pick and choose the elements of Marionette that you want to use in your app.
Events
Events are tie our whole app together, so it’s worth a bit more explanation. Marionette events are handled through Application.vent (gmm.Viewer.vent in our case).
The syntax is simple: vent.on(
MenuModule
The menu module is more or less the same, so I’ve gone ahead and implemented that as well. While coding things, I made some additional changes from the layout on the first post – I swapped out the original images used in the first post with icons in a web font, and added some CSS3 transitions. Checkout weloveiconfonts.com, as they’ve got some cool, free, icon font’s just set up and waiting to be used!
Summary
Ok – this has covered a lot of terriory – so lets review: We use modules to separate out the concerns of our application. We add modules to an application via an Immediately Invoked Function Express (IFFE) that creates the module. Inside the module we create a controller to do the orchestration of our views and events. In this post we really just created static views – the collections that back them do not change, and they are only rendered once.
Up next we will tackle the Search tool. When the user clicks this tool, we will show a “dialog” in the middle of the screen with a text box, then send that value (hopefully an address/place) over to the Esri geocoder, show a disambiguation list if necessary and then raise an event that the map will handle when we implement it. This should be a good way to cover some aspects of “dynamic” data with this framework.
Code
Code for this post is tagged v0.0.2, and it’s also available at http://dbouwman.github.com/geomacmapper