Don’t Execute A Backbone.js Route Handler From Your Code

I was working on a sample Backbone.js application and I ran into a scenario that seemed like it should have been simple on the surface, but was causing me a tremendous amount of headache. Here’s the basic functionality that I was trying to achieve:

  • Have a list of items displayed on the screen
  • When an item is clicked:
    • the item is highlighted
    • the application routes to the item’s view url

This sounds like a fairly trivial list of requirements, right? I thought so, too, until I started got down into the weeds of the implementation. For the sake of this blog post, let’s say the application is a … blog. The list of items is a list of blog posts, and clicking on a blog post will highlight the post title in the list, and then display the contents of the post, etc.

A Quick Note On Event-Driven Architecture

I’m a follower of the philosophy that a backbone.js application is an event-driven application that responds to changes in the state of our models. That is to say we don’t want to have our views manipulating themselves and referencing / controlling each other directly. Rather, we want to have our views call methods on our models that manipulate the state of our models. In response to the state of our models changing, our application does things. This could be updating some visual element, routing to a new hash fragment, and/or anything else we can do in a web page.

To facilitate this philosophy, I use a lot of events in my application design – but not just model events. I rely heavily on application level events, too. Before you go on, it’s worth your time to b familiar with the use of an event aggregator to facilitate application level events. I’ve blogged about using an event aggregator with backbone.js in the past, and you should read that first, if you haven’t done so already.

The First Implementation

In the first implementation of my requirements, I implemented the links to the blog posts not as hash fragment <a href> links directly, but as events handled by a view, which tell the model it was “selected”, triggering a “post:selected” application event which is handled by a method that called router.navigate. At a high level, here’s what that code looks like (I’ve left out a lot of detail from this code, to only show the important parts).

BlogPost = Backbone.Model.extend({
  select: function(){
    this.set({selected: true});
    this.collection.selectPost(this);
  }
});

BlogPosts = Backbone.Collection.extend({
  model: BlogPost,

  selectPost: function(post){
    this.vent.trigger("post:selected", post);
  }
});

BlogPostPreview = Backbone.View.extend({
  events: {
    "click a": "postSelected"
  },

  initialize: function(){
    this.model.bind("change:selected", this.highlight, this);
  },

  highlight: function(model, selected){
    var cssClass = ""
    if (selected){
      cssClass = "highlight"
    }
    $(this.el).attr("class", cssClass);
  },

  postSelected: function(e){
    e.preventDefault();
    this.model.select();
  }
})

BlogRouter = Backbone.Router.extend({
  routes: {
    "/post/:id": "showPost"
  },

  showPost: function(id){
    var post = this.posts.get(id);
    // load the post view and display it
  }
});

var vent = _.extend({}, Backbone.Events);
var posts = new BlogPosts({vent: vent});
var router = new BlogRouter();

vent.bind("post:selected", function(post){
  router.navigate("/post/" + post.id);
});

From what I’ve seen, this code is an example of how a lot of people are using a router to navigate directly to a url fragment instead of having an <a href> link that points directly to the hash fragment. Sure, I’m adding a few layers of indirection with my events in order to support the philosophy I want, but the end result is the same: click a link that is handled by a backbone view and (eventually) the router is triggered, to navigate to the right place.

The Problem(s)

Honestly, there are a lot of problems with this code and with the idea of having a view call router.navigate in order to fire off the route’s handler method and display something (even if the router is called through a few layers of indirection). I could talk about coupling, encapsulation and other issues. However, there’s one very important functional problem, from the user’s perspective, that I want to focus on.

When the user clicks a post in the list, the click causes the model to become selected (line 35) which highlights the item in the list (line 22, 25-30). It also causes the router to navigate to a url hash fragment that will display the post by retrieving it from the list of posts and displaying it (line 4, 12, 55, 44). However, when a user navigates directly to the post url hash fragment by pasting it into the browser’s location, clicking a link from somewhere else on the web, or otherwise heading directly to it, the “selected” code is never fired.

You might think that it would be easy to fix this, like I thought. You can just add “post.select()” to line 46 (right after “var post = …” in the “showPost” method of the router) and everything will work, right? This will cause the model to be selected which will highlight the post in the list of posts. I did this at first, and I thought it was working. But there’s another set of problems that arise because of this.

When the router fires the showPost method for the first time, calling the model’s “select” method will eventually cause the event aggregator to fire the “post:selected” event which will then call the router.navigate to try and navigate to this url hash fragment again. Fortunately for us, the router is smart enough to know that it’s already sitting on that url hash fragment and it won’t fire the route’s handler again.

In addition, when we click the post from the list, having the router.navigate method called to set the url hash fragment and execute the router handler, we are duplicating some things in our process. First, the post is already loaded up in memory – we clicked on it after all (this may not be an issue in some systems, though. Perhaps it was only a PostPreview model that was bound to the list). Secondly, the post was already selected. Firing the route handler causes the post to be selected again, even though it’s already selected. If the application has some visual styling or behavior that is fired when the post is selected – my application slides things in / out – then you end up with duplicated effects and strange visual issues.

Of course, the end result is what we want – we have a highlighted post in the list and we have the post being displayed. However, I’m not happy with what we had to do in order to get to this point. I’m sure the users won’t be happy either, when they see the double-slide-in visual issue caused by the post being selected twice. Yes, there are some workarounds for the double-select, such as ensuring the post can only be selected if it’s not currently selected. However, this sort of “gate” is only a workaround and not really an elegant design or solution.

A good design and implementation, in my mind, would avoid these types of problems entirely. We wouldn’t have to put in checks to make sure we’re only selecting an item that isn’t currently selected. We wouldn’t have to worry about calling select twice because our app wouldn’t do that. We wouldn’t have to worry about strange doubled-up visual effects, or hitting a route twice, any of the other issues that i’ve describe, or any of the other issues such as coupling and encapsulation that I haven’t described. This, however, is not a good design.

The “AHA!” Moment Regarding Router.Navigate’s Second Argument

The documentation for backbone describes the second argument for the router.navigate method as a way to trigger or not trigger the route’s handler method. In the past (and very recently) I’ve been really frustrated by the default of this second argument being “false”. That is, if you do not pass a second parameter to router.navigate, the url hash fragment and browser history will be modified but the route’s handler method will not be called. If you want the handler method to be called, you must pass true as the second parameter.

Why?! When would you ever want to call router.navigate and not have it call the route’s handler method?! And then it hit me like a brick wall that had been standing there the whole time, only I wasn’t paying attention and didn’t see it until I had already smacked into it: most of the problems that I was having were caused by the eventual call to the router.navigate, passing true as the second argument.

A Better Implementation

I decided to try out a different implementation, then. Rather then have my application eventually call the router.navigate in order to fire the route’s handler method, I would instead have my application respond to the change in state in order to show the selected post. Then I could call router.navigate without the second parameter. This would update the url’s hash fragment and browser history, but it wouldn’t fire the route handler and I would avoid a lot of the problems that I was currently working around. I also need to allow the router to still work when a user gets a link from somewhere else, or types the url w/ hash fragment directly into their browser, etc. If I was going to facilitate both of these scenarios, then I would need a common chunk of code that could be called from either entry point.

Armed with this idea, I came up with a new implementation that looked more like this:

BlogPost = Backbone.Model.extend({
  select: function(){
    this.set({selected: true});
    this.collection.selectPost(this);
  }
});

BlogPosts = Backbone.Collection.extend({
  model: BlogPost,

  selectPost: function(post){
    this.vent.trigger("post:selected", post);
  }
});

BlogPostPreview = Backbone.View.extend({
  events: {
    "click a": "postSelected"
  },

  initialize: function(){
    this.model.bind("change:selected", this.highlight, this);
  },

  highlight: function(model, selected){
    var cssClass = ""
    if (selected){
      cssClass = "highlight"
    }
    $(this.el).attr("class", cssClass);
  },

  postSelected: function(e){
    e.preventDefault();
    this.model.select();
  }
})

BlogRouter = Backbone.Router.extend({
  routes: {
    "/post/:id": "showPost"
  },

  initialize: function(options){
    this.controller = options.controller;
  },

  showPost: function(id){
    var post = this.posts.get(id);
    post.select();
  }
});

BlogController = function(){
  this.showPost(post){
    // load up the post view
    // and display it
  }
}

var vent = _.extend({}, Backbone.Events);
var posts = new BlogPosts({vent: vent});
var controller = new Blogcontroller();
var router = new BlogRouter({controller: controller});

vent.bind("post:selected", function(post){
  controller.showPost(post);
  router.navigate("/post/" + post.id);
});

Notice that there are very few changes in this implementation. We’ve introduced a BlogController object – which, by the way, does not extend any backbone class because it does not need to – that removes the logic to display a post from the router (good encapsulation / single responsibility, etc). We’re no longer passing true as a second argument to the router.navigate function, in our handler for the “post:selected” event. And, the show post route’s handler method is not directly calling any view logic. Instead, the router is simply setting the state of the post by calling the post.select() method. From there, the standard logic to respond to the application’s state change kicks in, and the post is displayed.

Now, I know that calling “pos.select()” from within the route’s handler will still cause the router.navigate to fire. Once again, though, the router is smart enough to not do anything since we’re telling it to navigate to the hash fragment that we’re already on. This code isn’t “perfect”… but it’s still far better than what we started with.

Learning A Lesson About Router.Navigate

A router serves two purposes in a backbone app. The first purpose is to be a clean entry point for urls with hash fragments that are entered directly into the address bar of a browser, or clicked as links from external sites. The second is to manage the history of the browser, as your application moves between pages. Don’t mix these two things together. You shouldn’t be executing the route’s handler from within your application, most of the time.

Sure, you will need to call router.navigate to set the url fragment to an appropriate value at times, but you should listen to the defaulted second argument value and ask yourself if you really need to override this or not.

Don’t Fire Route Handler Methods From Within Your App

In all but the smallest of sample applications and special cases where there simply is no alternative that could cause the app to function correctly, I say the default value of “false” is the correct value for the second argument of router.navigate. By removing the “true” argument from the call to router.navigate, I was forced to find a different way to enable my application’s functionality. In this case, the introduction of a “controller” object (which has been greatly simplified for this blog post) allowed me to keep my code well encapsulated and provide a single logic path for the selection and display of a post.


Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Derick Bailey

Derick Bailey is an entrepreneur, problem solver (and creator? :P ), software developer, screecaster, writer, blogger, speaker and technology leader in central Texas (north of Austin). He runs SignalLeaf.com - the amazingly awesome podcast audio hosting service that everyone should be using, and WatchMeCode.net where he throws down the JavaScript gauntlets to get you up to speed. He has been a professional software developer since the late 90's, and has been writing code since the late 80's. Find me on twitter: @derickbailey, @mutedsolutions, @backbonejsclass Find me on the web: SignalLeaf, WatchMeCode, Kendo UI blog, MarionetteJS, My Github profile, On Google+.
This entry was posted in Analysis and Design, AntiPatterns, Backbone, Javascript, Model-View-Controller, Principles and Patterns. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://blog.wekeroad.com robconery

    I think I understand where it is you’re coming from on this, but I’m having a hard time following the reasoning. Let me see if I can explain why…

    At the outset, it seems like you’re trying to get a Master/Detail thing running here. All the posts are in memory and you want to display the details on selection.

    I think where it goes off the rails for me is this:

    showPost: function(id){    
    var post = this.posts.get(id);    
    post.select();
    }
    The *Router* is calling a method on your model … to facilitate a view change… hmmmm. I *completely* understand the conundrum you face here – I did as well with a Tekpub app I built (admin stuff) BUT – I went about it a bit differently.

    If you embrace the idea that Views beget other Views, and like we went over in one of the other BB posts you wrote that Routes are for a given “state” of the app – it seems to make perfect sense to me that a View can change the state of the app.

    In the write up here, you mention that you were using anchor tags to force the hash navigation – which is a bad idea. You don’t want to do this because the pushState=true might be set and that would change the way URLs are created.

    But the idea is sort of the same – in HTML we understand the view changes the app state (by clicking a link). Why not just go with that?

    I don’t agree with your definition of the Router (that it’s a starter point and a history manager) – I don’t think it’s a full Controller, but I think overall the idea is that it’s supposed to “manage” the navigation and “flow” of the app. Telling it to navigate seems perfectly reasonable if the event calls for it (clicking a link for instance).

    Think about the level of misdirection caused by the code in the comment above. Think about the dev who’s picking up your app, trying to follow the logic you laid down… it’s sort of convoluted. Whereas “you click a link and the page changes” has been with us for a long, long time …

    • http://mutedsolutions.com Derick Bailey

      first: sorry for the long, rambling response… “i didn’t have time to write a short letter, so i wrote a long one instead” … or something like that. i’m still trying to figure out how to best express my thoughts and opinions on this. can someone post the “TL;DR” version of my response as another comment, for me? :)

      second: there’s an underlying assumption in everything that i wrote in the post, and am writing in my response: an application’s display (html/css) is a visualized rendering of the current state of an application so that the user can make additional decisions for the application to process. i’m facilitating this by using a simplified event-driven architecture, where the application’s “business logic” (very loosy-goosy term) is responsible for setting the app’s state and the rendered html/css is merely a representation of that state.

      i don’t think i did a good job of explaining that assumption in the post, though i tried to

      warning: i’m going to split some hairs on the point of “a view can change the state of the app”. i think it’s important in this case…

      the html/css displays the current state of the app and provides the user with options that may (or may not) change the state of the app. when the user chooses any of those options, the view object (which is acting as a “presenter” or “controller”-like object) coordinates the action that the user wishes to take with the models and other objects behind the scenes. once the models and other objects have correctly update the state of the application, the view objects render any necessary changes to the html/css, based on the new state of the app.

      you say “in HTML we understand the view changes the app state (by clicking a link)” and i agree with this to the extent of the user’s perception of the app. the fine little hair that i’m splitting, is the one that separates the “a href” link that the user clicks on from the javascript that responds to the click and tells the app what the user wants to do, so that the app can update itself accordingly.

      i think my router definition is good. allowing anything more than this is starting to mix too many concerns into the router. in some very simple (not necessarily “small”) applications doing more in the router is not likely to cause many problems… there’s no hard and fast rule to say you can’t write more code in the router of course. i still think there’s still value in the idea of routing an application’s state, too. my understanding is a little more nuanced at this point, having run into issues caused by this.

      look at where the router comes into play, from a straight url perspective. if we paste “#/post/1″ directly into our url, the router is seeing a request for an application state. since the request came in “cold” (i.e. not fired by an event in the app), the router must call some code that can rehydrate the requested state of the application. once that state is rehydrated, the application’s existing code should take over and respond to that state in order to view. if we take that perspective and look at a link in the application that a user can click on, i think it verifies the focus of our router. 

      say the application is already loaded up and has previously rendered the state of the application for the user to see. the user clicks on a post in the list and, like you said, this is an intent to change the state of the application. we have a few choices at this point. we can have the application modify it’s own state and re-render the html/css based on the new state. or we can have the router navigate to a tokenized representation of that state, at which point the application needs to rehydrate the state that was requested via the token (the url hash fragment). the difference in these two choices, in my mind, is indirection. 

      by having the application update it’s state directly when the link is clicked, we are leaving out a level or two of indirection. the state changes and the view objects respond accordingly. we even have the router coming in to play, to update the url’s hash fragment and create an entry in the browser history. 

      if we were to take the path of router.navigate when the user clicks the link, we’re jumping from the internals of the application back out to the external perception of the application (the url hash fragment) in order to have the application dive right back down into the internals, rehydrate the tokenized state and render the html/css for that state. this seems like an unnecessary level of indirection. 

      why bother jumping out of the app’s state, up to the browser, just to have the browser tell the app that a new state was requested? why not have the application make the requested state change at the time it’s requested, instead of adding a level of indirection (that, incidentally, caused some strange issues in my app)? the only benefit you get by not using “#/post/1″ directly in the “a href”, compared to using router.navigate to move to the new state, is support for pushstate. i don’t think everyone needs (or knows about, for that matter) forward support for pushstate. (i’ve advocated doing this for the same reason a few times… but i’m slowly changing my opinion on this, too)

      also note that I’m not saying “dont’ use the router in your code”… you can see in my “better example” (which is not perfect, of course… still a few little issues in it) that i am calling the router from my code. but i’m calling the router in response to the application’s state having been changed so that the router can properly update the url’s hash fragment and create a browser history entry.


      regarding the indirection in my example (“misdirection”?), the code example in this case was poor. in my actual app my router makes a call that looks more like this:showPost: function(id){ this.controller.showPostById(id); }it’s only 1 layer of additional indirection as the showPostById method makes the call to post.select(), but i think it helps to clarify the intent. a developer coming into this project can see the method call in the router and know “ok, this route displays the post using the id from the route”.

      hopefully that all makes sense. i’d love to see more about the issue that you ran into in your admin app, and how you solved it. i’m certainly expressing some strong opinions here, but i’m always open to being shown where i’m wrong and changing my mind :)

    • http://mutedsolutions.com Derick Bailey

      FWIW: i’m starting to think there isn’t a “right and wrong” way to approach this… instead, there are just different approaches that are better suited for specific circumstances and architecture / implementation preferences

    • DynamicDan

      I am working on upgrading a flash site using hash URLs to an HTML site using hash URLs and it is turning out to be complicated for similar reasons as documented in this post.

      I need to ensure the following scenarios are possible
      - links to pages load the page via AJAX
      - links to in-page named anchors move the user to that anchor while also updating the URL
      - typed in URLs (for pages or in-page anchors) work always (or at least for the first page load)

      I have come to the conclusion that the controller needs to handle all URL display and logic routing of any link click event. Here are the JS steps that should be made:

      1 – upgrade links to required format (eg, #/stockists/victoria changes to #/stockists/internal/victoria)
      2 – all link clicks are captured for processing (no direct URL manipulation from links)
      3 – URL ‘view’ is updated using .navigate(url, {trigger: false});
      4 – when a user interacts with the address bar (usually a first time load) the event should be acted apon as if a link was clicked (true?)

      Point 4 is a little tricky.

      Thoughts?

      • http://mutedsolutions.com Derick Bailey

        Hi Dan,

        I’ve written some additional articles that take this idea and put it in to more practical terms w/ code :) you might them useful:

        http://lostechies.com/derickbailey/2012/02/06/3-stages-of-a-backbone-applications-startup/

        and

        http://lostechies.com/derickbailey/2012/01/02/reducing-backbone-routers-to-nothing-more-than-configuration/

        also check out my BBCloneMail sample app, which is an implementation of all my latest ideas. It might help you see how I’m solving this now:

        https://github.com/derickbailey/bbclonemail

        • DynamicDan

          Thanks, I do some reading and playing with the app.

          Once thing that is still not clear is what happens with the traditional use of ‘#/in-page-anchor’. We can’t let the browser scroll down the page and update it’s URL on it’s own anymore right? We have to override all links events, pass them to a router, work some magic (eg, JS scroll down the page) and then update the URL.

          True?

          • DynamicDan

            *do = did, Once = One, it’s = its, links = link …. where’s the undo/edit btn (within 2 mins)?

          • http://mutedsolutions.com Derick Bailey

            :P i think Disqus lets you edit comments if you register an account with them.

          • http://mutedsolutions.com Derick Bailey

            although i haven’t tried this, i’m pretty sure you can have in page named a # tags and also use routers. for the in-page tags, you wouldn’t specify a route that matches. if there is no route that matches, the router will ignore it and this should let the browser scroll down to that part of the page

  • http://gilesbowkett.blogspot.com Giles

    this is a good blog, but that donkey wearing a Mexican hat looks incredibly and incomprehensibly racist. I say this partly because I’m offended but also just because I’m baffled. I just can’t imagine what it must be like to live somewhere an image like that is socially acceptable.

    anyway, sorry, because I felt I had to say something. I do like your blog otherwise, your post on not limiting Backbone apps to Backbone constructs especially. it’s a very good point.

    • Anonymous

      @Giles Thank you for voicing your concern about the emotional impact that “Pablo”, our masot, has had on you. 

      Please understand that I (a mexican-american) created the mascot in good fun (based on how I look) to project the South Texas nature of the orgnazation and fun filled jovial atomosphere the community creates towards professional programming. Over the past 6 years we have expanded well outside the realm of influence of South Texas but the group felt that Pablo personified our approach towards making programming fun.

      Since then Pablo has become a brand of sorts and a persona unto himself. For example at the end of September we our hosting our annual open spaces conference: http://lostechies.github.com/fiesta/ entitled “Pablo’s Fiesta”. The community has come to enjoy our conferences and resonates the personification of the mascot in other areas such as training, design standards that are Pablo approved. :)

      I hope you take this comment as a respectful aknowledgment of your perspective with an emphasis towards drawing greater clarity as too the character and make up of LosTechies and it’s brand.

      Sincerely,
      Joe Ocampo 
      Los Techies CoFounder

    • Anonymous

      The about page has a short explanation on what LosTechies is about. http://lostechies.com/about-us/

  • http://pulse.yahoo.com/_MGXJ5ZRSD7ZCWNBZ6MPL2X2MIE shammondca

    Cheers on the on the Backbone posts as I hope more people are brave enough to take on the effort to forge ahead in this area. 

    I’m approaching backbone using CQRS, taking the idea that events can happen but there is a time when replaying all your events gets complicated. So you save a snapshot. To me Routes seem like a case for snapshots. There may be boiler plate code for responding to and representing the state your snapshot is in.

    Concerning your post I think agree with Rob’s statements about navigation being something should be represented in your views. I understand  things get complicated when you cross your internal events with routes, but I wouldn’t want to trigger an internal event from the call generated when a route is called. But I could see myself passing a hyperlink or route table to my view. And in the case where views need to represent data from a route table I would think that internal view on init would respond to the existing state.

  • Margie Roginski

    I have been going through all of your backbone-related blogs, and first – let me just say they are *so* helpful!!!  The best thing out there as far as I can tell. 

    On this particular blog, I am having some trouble understanding what displays when “the application routes to the item’s view url”.  You say that first the item is higlighted, and then the application routes to its view url.  I am confused as to where that view url displays?  IE, is it inline on the existing page (via an ajax call that puts the result somewhere on the page), or is it a new url.  Perhaps I just don’t yet understand how to utilize routes …

    Any chance you have the full code for this example – something that could be downloaded and run?  That would be very helpful.  Would be great to have it for both the “first implementation” and the second.  Another small detail that I don’t understand is how the BlogPost gets its this.collection attribute set.  For example, in BlogPost’s select() function, you have this.collection.selectPost(this).  Where is this.collection set?

    If you have more detailed source, it would be really great to be able to download it and run it – it’s so much easier to understand when you can be hands-on. 

    Thank you so much for your very useful blogs!

  • Kaha

    I coudn’t find any contact us page, so I’m writing it here. Link to Backbone Model Binding http://lostechies.com/derickbailey/category/backbone-modelbinding/ seems to be not working or broken.

    • http://mutedsolutions.com Derick Bailey

      doh! thanks for the heads up on that. i’ll look into it and see what’s happening with it.

      • Kaha

        Thank you for your great articles. I was just wondering, do you have any articles in that category? or I can find all backbone.js topics in Backbone category ( http://lostechies.com/derickbailey/category/backbone/ )?

        • http://mutedsolutions.com Derick Bailey

          they should all be available in the backbone category, but you’ll have to search through the posts to find them. 

  • http://www.facebook.com/profile.php?id=694648721 Vikram Malhotra

    I am having a similar problem where if user pastes a url of post into browser address bar, the post does not appear highlighted. However, I could not understand your solution of using ‘Controller’ object? When does it come into play in two scenarios – when user clicks on a post & when user directly navigates to the url of post?

  • Anonymous

    First I have to thank you for these backbone posts. I would be so lost without them. Second, I have question. I cant seem to follow how you are unselecting the link that was previously selected. 

    I think my problem is my inability to understand the following line:

    highlight: function(model, selected){ …}

    When does this function ever get called with the “selected” argument equal t0 “false.”

    • http://mutedsolutions.com Derick Bailey

      this code doesn’t show it, but when you call image.set({selected: false}); or call image.unset(“selected”), that method would fire, deselecting the image, visually. 

      i’ve been pondering a re-write of this post as I think i can write it much more clearly, now… i really should do that.

  • Kevin Warner

    Hi Derick… Great posts, very helpful. I have a question about whether the following approach would a good one. I am just getting in to routes, so bear with me.

    I have a router looking for routes like…

    /#inbox
    /#all
    /#important
    etc.

    These represent different views (not backbone views) in my app. So, my router handles each of these routes and calls a method in the router for each one.

    Within these router methods, I simply update a model I call ApplicationState. This has a few different members, one being “view”, which represents the selected view for the app.

    Then, I simply have my backbone views in the application listening for changes to the ApplicationState model, and rendering when the state changes. So, for example, I have a bullet list that acts as a menu, with links for each item (inbox, all, etc.) I also have another view that represents results based on the selected view.

    Why not just have the hash fragment right in the href. When the user selects a menu item (link), the browser’s url is updated (by the browser), the router fires, the state is updated, and all views listening for the changes in the state are updated.

    This appears to work when urls are pasted and also works during user interaction. Of course, there may be issues I have not run into.

    I am new to the router approach. My previous implementation was event driven but without a router.

    Thanks!

    Kevin

  • chandra

    This is good, but what about when you have to wait for an asynchronous response when the user submits a form to change view, the router needs to wait for the async success callback to fire before routing to the new view. I read your articles, and I see they are very helpful. Thanks!

  • http://www.facebook.com/goofydawg Brendan Delumpa

    Derek, I read this article with great interest several months ago. I agree with your overall approach in your final solution, but disagree with using the router as a secondary controller. I know you’ve cut out a lot of code for brevity, but the way you’ve coupled the model with the router is a bit disconcerting to me.

    In lieu of that, an approach I’ve taken to ensure that I have everything decoupled is to simply have the router trigger an event to which the application controller will respond and update the model(s) which in turn will update the view state. Conversely, a user clicking on a link, does nothing more than either updating its model, or triggering an event that the controller listens to, and it in turn instructs the router to update the URL. In either case, it boils down to a single point of control.

    So at least from my perspective, the router is simply a composite component with its own MVC within the context of an application controller. Its model being the browser history stack, the controller being the public API of the router, and the view being the URL.

    That’s not to say you can’t put all the controller functionality within the router – I’ve seen that done as well, though not very cleanly – but my personal preference would be to relegate controller function to a custom controller object.

    In any case, don’t know if any of this makes sense, but I’ve used this pattern with great success.

    • http://mutedsolutions.com Derick Bailey

      I think we’re both heading in the same direction and the implementation here is pretty lousy for all the reasons you pointed out. the premise of not using a router to navigate and not using a router as a controller still stand firm, but the implementation and example code in this post is outdated for sure.

      I’ve moved well beyond this example code and am doing much more controller-like implementations now, starting with http://lostechies.com/derickbailey/2012/01/02/reducing-backbone-routers-to-nothing-more-than-configuration/

      but I’ve also moved further along with the implementation of modules that i’m using, and having a module-controller per module, which handles all the work flow within a module. i havent’ blogged about this yet, but it’s something i’ve been doing in recent apps and i really like the way it works.

  • Edan

    Hi Derick –
    Thanks for the great article! Could you talk a little bit about directly accessing a route from the `href` tag? I know that your code snippet is a distillation of a more complex application, But I’m wondering if you could have, in this instance, created the route directly in an HTML template?

    eg. <a href="#posts/”>

    In other words, why do you need to bind this link in the javascript code at all? What are the advantages/disadvantages of this method?

    • http://simplynutty.com simplynutty

      @derickbailey:disqus What’s your take on this? I think I understand the example you used which is model-based but what about navigation links which aren’t really tired to a model? I have a few links in the header: #projects, #admin, #reports and I have a method which grabs the data-fragment value and passes it to router.navigate. I’d rather just use plain hrefs. Thoughts?

      • http://mutedsolutions.com Derick Bailey

        if you’re just grabbing the hash fragment and passing that to the router, don’t bother. just let the browser fire the navigation event when the link is clicked.

        this article is rather convoluted, and difficult to follow, honestly. i didn’t do a good of explaining the nuances and situations. i should re-write this at some point.

        • http://simplynutty.com simplynutty

          Thanks! The lack of “you have to code this way” is what I love about backbone but it’s also frustrating. I came up with at least 3 or 4 different ways to solve a problem I’ve been working on and could talk myself in or out of all of them. :/

  • maoshuyu

    test

  • Jake Smith

    Where would be a good place in the application to bind the “post:selected” event? Does it make the most sense to bind the event in the view that’s being loaded? That doesn’t seem right to me though.

  • http://www.joezimjs.com Joe Zimmerman

    Hey Derick,

    2 “problems” in your code:
    - In the controller, it should be this.showPost = function(post){… not this.showPost(post){
    - The router isn’t using the controller in this example, so there’s no reason to pass it to the router. I’m guessing that normally your routers will use the controller, but in this example, it’s pointless.

    Also, how about this instead: http://jsbin.com/avohet/3/edit
    Just look at the JS. It’s much simpler and straightforward.

    Here are my views on this subject that explain why I did it this way:

    There are two different sets of state: data state, and view/app state
    - The state of the data is controlled by the models and collections
    - The state of the app (which views are shown and where, etc) is controlled by the router and controller

    I tend to use a router/controller similarly to way they are used on the back end. When a user clicks on a link that should show them something new (change the app state but not the data state), the work should be passed off to the router and controller. Let the link route to its URL and the router see it and pass the work off to the controller. The controller will coordinate any state changes necessary.

    Why broadcast an app-wide event if you don’t need to? If you still want to let the app know something happened – so that other things can listen for it and react – do what you did already with the model’s ‘select’ method calling its collections ‘select’ method which broadcasts it. Or, for different types of events that don’t belong in the model/collection, you can fire it from the controller.

    In fact, in this example, I don’t even see a reason for the model being ‘selected’. The view should just react to the click and the router should react to the URL changing. If you want the routing to wait for an animation to finish then do something similar to this for the view: http://jsbin.com/avohet/4/edit

    This is one of the few instances where you would even want to call router.navigate: when you want to delay routing for a time.

    • http://thinkingonthinking.com/ Patrick Mulder

      Hi Joe,

      I was happy to see your comment, since the Controller concept is something which i am investigating for myself.

      I have made a small JSFiddle: http://jsfiddle.net/mulderp/Vhk8G/11/

      The idea is that the controller ‘controls’ the views; one difficulty I see right now is that kill views from a controller is not so easy.

      Well, would love to see some more discussion on the controller / coordinating problem for events and views.

      Cheers,

  • chickenfur

    Thanks for your post. It was very helpful.

  • Andrew Pavlov

    How do you manage blog post list itself? App must know how to render this by visit from outside of the app, so the page must be rendered entirely, and from inside of the app. In second case, for example, by click on item in blog-post-list, it is unwanted to re-render already existing list.

    One possible solution that I have is to teach views to calculate their ‘hash-value’ – that will represent their unique state based on their content and logic, and so, by comparing that has-values, rendering facility (like Marionette.Region) will be able to find out is two views – old and new – are essentially equal, and there is no need for re-rendering, or they are different, and old must be cleared.
    No sure if it will work in real-life, its just for representation of the problem :)

  • James Donaldson

    I have a general architecture question which I suspect is borne out of my using the router incorrectly, so I’m hoping you can comment on it. My application allows a user to search for information which I then display to them in a list. Every time I’m done retrieving the information (through an ajax call), I update the url with a path that would allow the user to bookmark and have the same search be executed if they visited that url directly. The problem is, if I click the back button and re-execute one of these routes, I create a new history entry, creating the effect of the user never being able to go forward through history, only backwards. Am I missing some fundamental understanding of how I should be using a router?

  • Scott Harwood

    I have had the same problem in the past but the issue I have with the implementation you have is it is not atomic. A better way I believe is to bubble the event up from the views themselves, eg itemClicked(id), to your module or app level. Then the only response to the event is Backbone.navigate (with trigger). When you respond to the url change, propagate the state down to all your models. This way you always ensure that your model state is in sync with the url.