2011S
Symfony Backbone Js for Highly Dynamic Apps
As I am currently starting my company (http://linads.com), some parts of the app I am building need to be highly dynamic. At the core of the app is a planning management that is pretty complex because it needs:
- To be active (users should be able to not only view the planning, but also create new bookings from there)
- To start from a yearly view (by month) but go into details at the daily level (showing all day of the years on one view is ugly and not usable, showing days within just one month is not enough as bookings might be longer than that)
- To show all current bids from customers on each possible item at any given time.
If you want something like this to be simple to use and browse through, you can’t show everything at once but need to let the user view more details where he needs to without ever leaving the page.
In this post I’ll try to explain how I use Symfony and a handful of JS libraries together to make this work.
Symfony and the templates
If you’re going to rely as heavily on javascript as this app currently does, most of the templating will be moved to javascript to let the app dynamically render some parts or others depending on user actions.
The templates I create are therefore very simple as they contain almost no data but only some empty elements that will be filled at a later stage once the page is loaded. At first I was rendering a base view with all the standard data, but this would mean that the javascript would have to parse the whole html to get this data and be able to make it dynamic. This approach is highly inefficient.
The better way of doing this is to add a script tag containing your data as JSON that can be directly fed to your javascript in a much more efficient way. You can see that the basic template is REALLY simple:
All it contains is a table called “booking-plan” that will later receive some data from javascript.
We’ll go back to add some things to this template a bit later as we go through the different javascript parts.
Backbone.js
Backbone.js is a lightweight javascript “MVC” framework. It’s not really MVC, but still helps you organize your JS code very well. Backbone has 4 base objects that are most important to me:
- Model: this will probably be similar to your symfony models
- View: controls a specific HTML element on the page
- Collection: a list of models
- Event: allows to create custom events on Models, Collections and Views.
Setting up backbone.js
First let’s see how to install Backbone. Backbone requires a few other JS libraries in order to work, and uses jQuery (or others) for DOM manipulation. So you need all those files. Personally I put them in /web/js/vendors/ directory. Requiring this many JS files will mean longer page loads, but but but… doesn’t Symfony2 come with an awesome tool called Assetic to help us with that? It does. Assetic can (among other things) help you combine all those files together for a production environment.
My policy here is to combine all “vendor” / third party javascripts in one (for jQuery, you can also get them from a CDN such as google’s own for example). And then all of my own JS are also combined into a single file. But for development, I might as well have all of them separated which is easier when I need to update just one of those third party scripts.
This goes in my base template layout. On a dev environment, each file is served as its own (though through a slightly different name). On a prod environment they’ll all be combined together. Also note it would be better to apply a filter here to use YUI compressor or Google Closure Compiler to minify the final script but I haven’t been able to make this work so far.
Also you can see that I use many JS libs. Most of them are required by Backbone. /plugins/* loads all the jQuery plugins I want. Then moment.js is something I need to deal with dates and time issues. ICanHaz.js will come in handy later, I’ll explain why.
Using Backbone
I’m not gonna create a full backbone.js tutorial here, there’s more than one available around and I just want to focus on how I integrate all of these together and keep things organized inside a symfony2 project.
Say you have created your backbone models, views and collections as you need to. Usually you will have a model view and collection for each component and a general Application View that will define the global behavior of your app.
As an example, for booking my media, I could have the following media specific code:
As you see the main view should be initialized with some JSON data. The ‘initialize’ function is not the constructor but is called by the constructor. The constructor has saved the arguments under ‘this.options’. In this case, the constructor should be called with a JSON list of media. Each media will contain an id, a code, and a list of bookings. Then while instantiating a media, a list of bookings will be created in much the same way.
Feeding Backbone initial data
What you could do is just have this, and then use Backbone’s ajax capabilities to retrieve all the required data by calling the server again. However since we’re rendering the page once already, we can feed initial data at the same time.
This can be done while we render the symfony template by just adding this:
Here I render another template which returns all my needed data as JSON. I keep this in a separate controller as I might want to update this data from the client at some point and therefore need a real action / controller / url for this job.
That’s it, the initial data is there.
Some additional sweetness
I didn’t show the details of the render method on a view here. Backbone does not care how you render things. You could use a javascript templating engine or build everything yourself. You could make a call to your server to retrieve the html, you could …
I have a few issues with this:
- I don’t like to have half of my templates in Twig and half of them in some JS files
- JS can’t have multi-line strings and templates become impossible to read
There are many template engines for JS out there. There’s even an effort to reproduce Twig syntax for JS templates (I haven’t checked it yet).
I chose to use mustache.js as my templating engine in javascript. But this would mean I still have the 2 drawbacks I just explained up here. For this I use ICanHaz.js which lets you define those templates in your html file. Now all templates are in the same place, I can write them on more than one line. ICanHaz lets you define script tags containing your template, give them an id and then directly use the javascript function ich.my_template_id(data) to render it.
In twig this goes like:
And then in your javascript the render method would be:
Perfect! And we’re almost done!
Keeping it organized
As you start writing more javascript, you don’t want to keep everything in the same file. It will become messy, super long and unusable for development. To keep things organized, you should split your files (we’ve seen already that Assetic will let us re combine them for production anyway). Another good thing is to make use of modules.
Very briefly to use modules, you start by creating a variable, your namespace that will contain all your code, and give it a method to return a module:
Then each of your module is defined in a self executing function:
That’s a good start, now let’s split our files and have them organized. I put all my JS in the bundle they’re related to:
- /MyBundle/Resources/public/js/app_main.js
- /MyBundle/Resources/public/js/media/media_model.js
- /MyBundle/Resources/public/js/media/media_list.js
- …
Now that everything is more clear, a bit of assetic magic and we’ll be done:
Conclusion
It took me a little while to figure out how to make all these things. At first you don’t have the need to organize much, then your script gets bigger and you get lost as to where are your models / views etc…
Backbone is a very nice thing to work with but takes some time (for me) to get to understand. However once you get it, it is a real pleasure, and it lets you do things the way you want. Frameworks like JavascriptMVC, sprout core and others seemed overly complex to me, and you don’t know where and when you’ll be able to customize things.
I hope this helps some of you try it and not just for sample TODO list applications but real life apps!