Backbone in the wild, lessons learned using backbone.js
Getting Your Head Around Client-Side MVC
Coming from a world of server-side templating, models, and views with Django, I had to start thinking of how that translates to backbone. Your backend will mostly be an API that serves data based on the requested URL and your business logic. Using backbone, my django project only serves one template for the whole app. Everything else is handled by backbone. That should give you an idea of how much heavy lifting backbone is able to do. I ended up stripping 70% of the view code I had written in django.
Oversimplified Explanation of Backbone
Here is my grossly oversimplified mental model of what backbone is.
- Models: serialized data that describes an instance (similar to a django model without the database instructions)
- Collections: a list of models (kind of like a queryset)
- Views: a handler for a section of your app (similar to a django view, but typically more granular)
- Router: routes a url to the appropriate view using a hashbang or pushState
- Templates: Sticks you data into html. Not a part of backbone, but you will almost certainly use them. I personally use handlebars, but you can use whatever you want.
Backbone Patterns No One Tells You About
Going through the tutorials and reading other people’s code I’ve picked up on some basic patterns that are used by convention. Some of them were not immediately obvious when I started learning. Others I learned the hard way.
- Granular Views - Consider a page that has a list of articles. With django you might create a view that grabs a queryset and outputs a template with a for loop to produce the list. You might then use jquery to handle events like the user editing that instance. With backbone you would set it up like this: A list view creates a collection of articles, for each model in the collection create an article view. Notice that there are two new pieces in there. The collection which is tied to a url which will call your api then automatically create a list of models from the received data and a view for an individual article. It was not obvious to me why in the world you would want a view for an individual article which might be 5 lines of code. The reason is for event handling. You’re basically setting the scope of events that apply to that code. This will help you avoid large, complicated views that try to handle all sorts of things for individual items. In the long run this leads to better separation of the different parts of your app and makes it much more manageable. Seems like more work up front, but it makes it easier in the end.
- Zombies - I wish the documentation was more up front about this. Any non-trivial app typically creates and changes many views within a single session. YOU HAVE TO CLEAN THAT SHIT UP. If you don’t, your beautiful modular code will exhibit bizarre behavior as soon as you create a new view without first removing the old one. Events are delegated to elements. Even though you may have cleared the html, you have not removed the delegated events. Before you instantiate a new view that replaces another one, remove it and unbind all events from it. (this.remove() this.unbind()). It’s really hard to debug this, but the symptoms are usually things like the wrong ID being used, wrong model is changing etc. Zombies. For more info read this.
- Requirejs - Non trivial apps will need to be separated into multiple files. The tutorials out there fail in explaining this. You will almost certainly be needing requirejs. Learn require well. Took me an embarrassing amount of time for me to get this.
- One view per file - Related to the requirejs tip above, you only put 1 view in per file. Coming from django I just thought I would put a whole bunch of views in one file, but that’s not how it’s done with backbone using require. One view, model, collection, per file. Why? Because using require you need to return a class and it can’t handle you returning multiple classes.
- Initializing an app - Using require, it’s not obvious how you “boot” the whole app. Create a view that will represent your app and create an initialize function to do what you need to start it up. In your main.js (used by require) create an instance of your app and voila, it will initialize. Typically you would initialize the router and any views that must be there no matter what. For Edorati that means the sidebar and router.
- Router is your friend - Let your router handle the switching of views. Don’t try to stick the router logic into your views. Need to send people to a profile detail page? Create a route for it and use that url in the template. Don’t create an event handler in your view so that when someone clicks on it it creates a new view. Just use the router for what it does, route things. Doing this also means you’ll have re-usable links that can be used outside of the session. i.e. link that directly takes you to a user’s profile.
I’m sure I’ll think of more, but that’s it for now. Most of that list will not make sense until you dive into backbone creating your app. Tweet me if you have any questions @alexkehayias.