

Single Page Web Apps with Backbone.js - ashrust
http://blog.sendhub.com/post/19349219519/single-page-web-apps-with-backbone-js

======
gbadman
I built <http://plunker.no.de/edit> using Backbone.js and really have enjoyed
the process. The addition of the sync event has really helped me out.

That being said, I think the most important abstraction that I introduced in
this project and not in others was inspired by moviepilot.com's Chaplin
project: <https://github.com/moviepilot/chaplin>

The revelation was to have a central mediator pub/sub mechanism. I used that
central mechanism to allow different parts of the ui to declare intents. Each
intent has a target that can handle that intent and (if appropriate) emit a
corresponding event. The idea is that there is a single handler of each
'intent' and can be many handlers of 'event's. This is because each UI
component doesn't necessarily know of the final receiver of the intent events.
Keeps things nicely decoupled (so far at least!).

For example:
[https://github.com/ggoodman/stsh/blob/master/assets/js/views...](https://github.com/ggoodman/stsh/blob/master/assets/js/views/sidebar.coffee)

The Sidebar contains a list of filenames in the current 'plunk'. When the user
clicks on a filename, an 'intent:activate' event is fired. When the user
double-clicks an 'intent:rename' event is fired. Only once those changes have
been handled and refired as 'event:activate' or 'event:rename' are they
reflected in the UI.

~~~
jules
What does intent:X do besides firing event:X? i.e. what does intent:X buy you?

~~~
gbadman
That's a good question. The Sidebar example should illustrate this pretty
well.

Current scenario:

1\. The user clicks the remove file button [-] at the bottom of the Sidebar.

2\. The Sidebar fires an 'intent:fileRemove' event through the mediator.

3\. The handler of this event can then determine which file should be deleted
and can also prompt the user to confirm their intentions.

4\. If the user confirms that they want to delete, then the model is changed
and the 'event:fileRemove' event is fired off.

5\. The UI then reacts to this event.

If the Sidebar instead fired of an 'event:fileRemove' directly this would lead
to some issues:

* The Sidebar would need to take over the responsibility of prompting the user about file deletion.

* If I add a new UI entry-point to file removal, this user prompting behaviour needs to be duplicated.

TLDR; It buys me extra decoupling between user intentions (UI -> Model) and
state changes (Model/Controller -> UI). It lets me add a Menu with a Remove
item without needing to change my controller and model to accommodate it.

------
drags
_A good example for using the dispatch object was how views interact when we
send messages out from the compose message form on Sendhub.com. When a user
composes a message and submits the form (ComposeMessageFormWidget), we render
the new message in the ThreadView, which is a different view object. The
ThreadView object listens for new messages to be sent during render_

Another, less complicated, way of achieving this is to have the view listen
for model attribute changes.

Presumably the compose form generates a Message model which is part of a
Messages collection. Both ComposeView and ThreadView can have access to (and
listen on) this Messages collection.

Then when a Message is successfully created server-side (or when it's created
client-side), a "saved" attribute on the Message model changes to true. This
bubbles up through the Messages collection and ThreadView's listener gets
tripped with the relevant Message as an argument.

------
sophacles
So backbone is pretty awesome. I've recently been basing a project on it, and
I really dig the simplicity it affords. One thing tho, which I feel it could
really help is progressive enhancement, but it seems that the case is not on
the official radar. Let me explain a bit what I am looking for, related to a
different project:

I am building an informational site for an org run by a committee. This is all
volunteer, and there are several people who can do changes to different parts
of the page. I know that a CMS is the traditional solution here, but the
combination of no plugins to do some of the specifics needed, and the fact
that a CMS is too heavy-weight for the rest of the use cases, have me looking
at a custom solution. (To be fair, my desire to play with some of the
underlying tools I'm using also has me doing this... but whatever its
volunteer :) ). So backbone is really nice for this case because:

* The collections and models work great when someone is in a content editing role/mode.

* The data on the page sorted as collections and models work great for things like sorting, sub-searches, etc when JS is enabled.

* For the above use cases, the hash based standard backbone routing is exactly what is needed.

However:

I would also like to have sub pages/content pages dynamically loaded in
combination with pushState when available, and different from the apps. When
pushState is not available, a new page load is OK, as linkability is
important. Essentially I want to keep the page loads with minimal transfer and
client side when possible, but the server will build the page outright if
content-type application/json is not requested. This is where backbone seems
to fall apart a bit. There is not a good router option that will do this and
work with the above described has routing as well. It seems that the backbone
folks don't want this use case so I am experimenting with building my own
router as a plugin.

I'd rather not do that unless A) there is desire from more folks for this and
B) there is not something out there already that works within backbone, or
nicely along-side it. Any thoughts from the HN community? (also sorry to
hijack the thread a bit)

~~~
jashkenas

        > I am building an informational site for an org 
        > run by a committee. This is all volunteer [...]
    

Use a standard CMS. You'll thank yourself later. ;)

~~~
ams6110
and the organization will thank you when you're gone...

------
malandrew
What I still haven't figured out is the best way to do responsive web apps
with Backbone.js. Off the top of my head I can only think of two, Flow and
AudioVroom. Flow uses separate javascripts for both versions. AudioVroom, has
one file, but it's minified and I haven't had a chance to trying to reverse
engineer it to see how they implemented the different layouts in a mobile,
tablet and desktop environment.

~~~
rdpfeffer
We're approaching that same subject now for our development this week. That
will probably lead to another blog post ;-) But we are having some luck with
Django Compressor and doing CDN hosting. RE:Different layouts...One thing
we've tried to do is rely on CSS as much as possible to do the heavy lifting .
As for re-using accross mobile and web, you might want to look into putting
all of your low level collection and model code into a git submodule or the
equivalent so that it can be re-used accross projects.

------
tristan_juricek
We ended up doing something similar here with multiple views. Similar, but
different. Our app is very desktop like, with basically a layout container
with sub-parts that re-render. Instead of a "widget" system, I created a
parent-child hierarchy, where parents would bubble child events on up. This
way, the router just receives app notifications from a 3rd or 4th level child
and can decide to "switch" the main view. The parents cleaned up the kids.

It worked for this app, but I don't think our pattern was particularly useful
unless you had to build the kind of complex "pseudo-multiframe" kind of app we
were building.

This is why I love the approach of backbone. It's easy to read, and establish
new patterns. Our "multiple view" pattern we're using is a little different
from what sendhub is doing, but I think it was fairly direct for both of us to
get what we want.

------
jcromartie
Just a stylistic nitpick: the CSS shadow letterpress effect is fine for titles
and other places where emphasis is needed, but it's just distracting when
applied to large swaths of code.

I tried to leave this comment on their blog but the only options were to
comment using Facebook, Yahoo!, AOL, or Hotmail...

------
kennystone
Great post. Backbone made all of my code faster and simpler to maintain, too,
and perhaps more importantly - easier to think about.

~~~
rdpfeffer
Same here! Once we embraced eventing, things became much more simplistic.

------
togasystems
It sounds like there needs to be a garbage collector for backbone. Is there
any projects out there dealing with 'Zombie' objects?

~~~
snprbob86
Here's what we did:

1) Derive all views from our own BaseView class (which derives from
Backbone.View)

2) BaseView's initialize function sets $el.data('view', this)

3) BaseView provides a `dispose` method which with some default logic for
event handlers added in a particular way.

4) We overrode jQuery.cleanData to check for the view data on each element and
call dispose on them. This ensures every view is cleaned up when it is removed
from the DOM via jQuery.

5) Use $.fn.remove and $.fn.detach appropriately.

This works splendidly in practice.

~~~
sgk284
I'm snprbob86's co-founder at Thinkfuse. Here's our actual jQuery cleanData
wrapper (in coffeescript): <https://gist.github.com/2046371>

    
    
      jQuery.cleanData = _.wrap jQuery.cleanData, (cleanData, elems) ->
        for elem in elems
          $(elem).data('view')?.dispose()
        cleanData.apply @, arguments
    

That lets jQuery handle garbage collecting automatically for us whenever the
Backbone view's corresponding element is removed from the page. Just make sure
your base view constructor sets the 'view' data attribute on it's element.

------
jwarzech
I've become a huge backbone.js fan over the last fews months. If your app uses
a lot of large forms I'd definitely look at the backbone.modelbinding project:
<https://github.com/derickbailey/backbone.modelbinding>. Also a few days ago I
released a plugin from one of my projects that adds data binding support for
deeply nested relationships:
<https://github.com/jwarzech/backbone.modelbinding.nested>

~~~
stusmith1977
Just to present an alternative: I found for apps which don't use many forms
the above plugins can be overkill. I used something like the following:

<https://gist.github.com/2049308>

..to provide simple form-to-model and model-to-form conversions, with
appropriate validation. Just decorate the form elements with data-
bind="property" and away you go. (I'm using Bootstrap so the validation is
tied heavily to that).

(I'm certainly not trying to denigrate the above plugins - obviously different
apps have different requirements and as pointed out, if you have many forms
then the above plugins make much more sense. The fact that Backbone lets you
make these decisions is for me a nice feature).

~~~
jwarzech
Nice and simple...I like it. Snippets like these are great to have in the
arsenal. Sometimes I end up using the 'heavy' solutions for simple things
(just cause I've used them before and its easy to snap in) when I could really
get away with rolling a simple solution like you did there.

------
gregarious
Good write up - keep them coming! We've done our experiments as well and plan
on moving more to this direction.

Would love to hear how you end up dealing with more nested collections and
model loading / persistence.

~~~
rdpfeffer
Thanks! RE: Nested collections, we ended up playing around with Backbone
relational as an extension to backbone (
<https://github.com/PaulUithol/Backbone-relational>) to deal with nested and
multi relational collections. In short, whenever faced with a more complex
relation, we took the easiest path possible which was normally a matter of
creating another collection relating two entities. As far as client side
persistence, we did not address that. Its on our backlog, but for right now we
are just relying on our API to serve up the right subset of data with every
call.

------
atomical
> DYNAMIC LOADING > Cons: Although the jury is still out on this, we have
> concerns that this approach may not scale to very large sites. As an
> application becomes larger and more templates become required, its easy to
> see how the main page load may take more time and increases the amount of
> load on your servers.

By main page load don't you mean first page load? Ideally the browser will
cache them as an asset package and that first page load will be a little
slower. But how would that put more load on the servers?

~~~
Osiris
That's funny. I'm working on a project where we're specifically using Backbone
in order to be able to scale.

We're compiling all our Backbone templates and pushing them to Akamai so that
user's around the world can get the JS templates from a local CDN and only
need to get JSON data from our main server. It's far easier than trying to
modify the app to be able to deploy across multiple datacenters.

------
enoughalready
Has anyone here tried optimizing initial page load by including the json data
passed to various templates as part of the page, rather than making a separate
request to get the data?

for example, what i've been doing is using a server side template write out
the clientside templates and initial json data used by the clientside
templates:

    
    
      <script type="text/javascript">
        var viewModel = {{- JSON.stringify(viewModel); }};
      </script>

~~~
jashkenas
Yes -- it's highly recommended to bootstrap all of the data you need for an
initial load, and avoid any extra HTTP requests. From the FAQ:

<http://backbonejs.org/#FAQ-bootstrap>

------
shutton
Good post. I'm similarly happy with how Backbone adds a lightweight structure
to my project and guides you into doing REST correctly.

I'm using it in a project which uses appcache and localstorage which means
return visitors can load the entire app without a single round trip to the
server and can even use it offline.

------
ezl
What impact does this have on SEO?

while I enjoy the use experience of fast loading single page apps, if it means
it makes a large amount of your content uncrawlable, that seems like a
worrisome business tradeoff.

------
sbouafif
I started to work on a similar project this week. This should be helpful.
Thanks for sharing!

~~~
rdpfeffer
Great! Glad to be of help. :-) Do post a comment on the article or send us an
email if you have any questions.

------
thomasdavis
No one else has mentioned using a view manager/factory to handle the life
cycle of views?

instead of

    
    
      var view = new SomeView(options);
    
      view.render();
    

we do

    
    
      var view = Vm.create('some descriptor', SomeView, options);
    
      view.render();
    

The factory/manager can do many things! List out active views, clean up all
views at any stage during their life cycle, default cleaning methods etc etc

~~~
stusmith1977
Agreed. Always keep SRP in mind. In effect, this is IoC for views. Having a
separate manager to manage views and their lifetimes is very useful. Need a
popup dialog view? Manager handles it. Views have prerequisite views (like
sign in, or loading)? Manager handles it.

------
funkah
Good stuff. I like the dispatch idea. I've encountered the need for a way for
views to talk to each other as well, though I ended up using another Backbone
Model for that.

~~~
aaronfalloon
We've employed this idea in Typecast (typecastapp.com) and it has proven
really useful. One thing to watch out for is how you namespace events... Make
sure and stick to a consistant format.

~~~
jashkenas
Typecast looks gorgeous. If you're interested in getting listed in the example
apps on the Backbone homepage, email me a brief description and a 550px-wide
screenshot...

------
pleasebehonest
Is Backbone.js worth it for interactive web pages that don't require posting
to a server?

~~~
jacobolus
Yes! But this depends a bit on the use case. The main benefit of Backbone is
that it helps you organize your code into separated data model and
view/interaction objects, so that you don’t wind up with a giant mess of
deeply nested, arbitrarily interconnected, and unmaintainable DOM event
handlers. It also has a nice object designed for coordinating browser
history/URL views in single-page apps. If any of those components would be
helpful to your project, absolutely try using Backbone for it

------
Domenic_S
So gross. Clicking "about" for example (from the main page) loads the about
content in a pokey 1.16 seconds, plus drawing time. There's no hint that the
content is loading.

While 1.16s is faster than a new page load, it feels crazy slow.

