1

If a user has bookmarked, or shared the following URL, how should my one page backbone.js application (uses Backbone.Router) go about retrieving the required data in order to render the complete view?

http://mydomain.example.com/menus#/menus/99/items/33/ingredients

If the user bookmarked http://mydomain.example.com/menus I can easily bootstrap the first page of menus.

# Inline before </body>, in a <script> block
Menus.reset(<%= @menus.to_json %>);

But in the case I mention above, there's 3 layers of bootstrapping required, yet since this is #hashtag navigation, the server does not know to bootstrap Menus, Menu of :menu_id, Items, Item of :item_id and the first page of ingredients.

# It would look like the following on a real page
# http://mydomain.example.com/menus/99/items/33/ingredients
# please see past the invalid syntax
Menus.reset(<%= @menus.to_json %>);
var menu = Menus.get(99);
menu.items.reset(<%= @menus.find(99).items.find(:all).to_json %>);
var item = Menus.items.get(33);
item.ingredients.reset(<%= @menus.find(99).items.find(33).ingredients.find(:all).to_json %>);

Is there a pattern that others use to accomplish this? Does this make sense to you?

I have a strong urge to create a bootstrap controller with the sole purpose of fetching all the models and collections required to draw any page. /bootstrap?path=/menus/99/items/33/ingredients and then have that return some jsonp to the caller page http://mydomain.example.com/menus#/menus/99/items/33/ingredients.

Thanks in advance.

Siguza
  • 21,155
  • 6
  • 52
  • 89
Mark Peterson
  • 570
  • 5
  • 19

1 Answers1

1

If I understand your question correctly, you have the issue where a deep link into the middle of your application hits the issue that a view cannot be sensibly constructed, since the data isn't loaded.

I think here you need to realise that there doesn't need to be a 1:1 correspondence between routes and views.

So when your router detects this situation, it can display a generic "loading" view, begin fetching the data, and then when the data has arrived, display the intended view.

(This mechanism can also be chained - so for instance you may want to show a login view, then a loading view, then the final view).

So I might have a utility function like so:

awaitFoos = function (foos, callback) {
  var loadingView = new CommonLoadingView();

  if (foos.isFetched) {
    callback();
  } else {
    this.showPageView(loadingView);

    foos.fetch({success: callback});
  }
};

And then in the router, I will use this:

goFooView: function (fooid) {
  var self = this;

  this.appView.awaitFoos(function () {
    var model = self.getFoo(fooid),
        view = new FooView({model: model});

    self.appView.showPageView(view);
  });
}
stusmith
  • 14,003
  • 7
  • 56
  • 89
  • I upped your answer because it's what I'm doing now (similar) for #/menus/99 so it's definitely not wrong (in my opinion) :) But it lends itself to a nested callback hell for #/menus/99/items33/ingredients – Mark Peterson Jun 01 '12 at 14:53
  • It does indeed. One approach (that I haven't tried yet) would be to use some form of promises/async library (eg JQuery promises, node-promise, or async [https://github.com/caolan/async]). – stusmith Jun 01 '12 at 15:26
  • I'll give one of the promise libraries a whirl. Thanks for the idea. Perhaps I'll create two routers. One that is used only on initial page load (think window.onload) to load all the required data for a page (#menus/99/items/33/ingredients) and then the other router for the user navigating through the application. I'm going to keep the question open, and hopefully more ideas come in. I can't imagine that I'll be the only person who ever has this question. Thanks! – Mark Peterson Jun 04 '12 at 17:55