15

I am trying to set up my Router to use "hashbang" URLs (#!).

I tried this, but obviously it doesn't work:

App.Router.map(function() {
    this.route("index", { path: "!/" });
    this.route("otherState", { path: "!/otherState" });
});

Is this possible to do in Ember?

twiz
  • 9,041
  • 8
  • 52
  • 84
  • I managed to get this to work by overwriting the `Ember.HashLocation` property to add some '!'s to several strings. I'm not entirely sure if this will end up breaking anything. If no one post a more legit way of doing this, I will add my code as an answer. – twiz Feb 18 '13 at 05:14

2 Answers2

19

Teddy Zeenny's answer is mostly correct, and registerImplementation seems to be a clean way to implement this. I tried to just edit his answer to make it fully answer the question, but my edit got rejected.

Anyway here is the full code to make Ember use hashbang URLs:

(function() {

var get = Ember.get, set = Ember.set;

Ember.Location.registerImplementation('hashbang', Ember.HashLocation.extend({ 

    getURL: function() {
        return get(this, 'location').hash.substr(2);
    },

    setURL: function(path) {
        get(this, 'location').hash = "!"+path;
        set(this, 'lastSetURL', "!"+path);
    },

    onUpdateURL: function(callback) {
        var self = this;
        var guid = Ember.guidFor(this);

        Ember.$(window).bind('hashchange.ember-location-'+guid, function() {
                Ember.run(function() {
                    var path = location.hash.substr(2);
                    if (get(self, 'lastSetURL') === path) { return; }

                    set(self, 'lastSetURL', null);

                    callback(location.hash.substr(2));
                });
        });
    },

    formatURL: function(url) {
        return '#!'+url;
    }

}));

})();

Then once you create your app you need to change the router to utilize the "hashbang" location implementation:

App.Router.reopen({
    location: 'hashbang'
})
Francesco Montesano
  • 8,485
  • 2
  • 40
  • 64
twiz
  • 9,041
  • 8
  • 52
  • 84
  • 2
    Great just a few comments: - It's better to reopen `App.Router` instead of `Ember.Router` (So you keep ember defaults clean, especially if you're testing) - You don't need to overwrite `init` and `willDestroy` since you are extending HashLocation, these functions will be inherited and the code would be simpler. – Teddy Zeenny Feb 19 '13 at 09:21
  • Thanks. I changed what you said. – twiz Feb 19 '13 at 21:01
  • 2
    This method works perfectly but in last ember throws a deprecation message; what do we have to do to use this without using deprecated code? – Cereal Killer Feb 03 '14 at 17:04
  • 1
    Just in case anyone was unaware, Google can also crawl sites that use HTML5 pushState rather than the hash-bang method. Here it is from the horse's mouth: https://www.youtube.com/watch?v=yiAF9VdvRPw Here is the Ember doc on using it: http://emberjs.com/api/classes/Ember.Location.html#toc_historylocation – twiz Mar 14 '14 at 21:12
10

Extending Ember.HashLocation would be the way to go.

For a clean implementation, you can do the following.

Ember.Location.registerImplementation('hashbang', Ember.HashLocation.extend({
  // overwrite what you need, for example:
  formatURL: function(url) {
    return '#!' + url;
  }
  // you'll also need to overwrite setURL, getURL, onUpdateURL...
})

Then instruct your App Router to use your custom implementation for location management:

App.Router.reopen({
  location: 'hashbang'
})
Teddy Zeenny
  • 3,971
  • 1
  • 17
  • 20
  • Good answer. I wasn't using `registerImplementation` in the solution I had. That definitely makes it cleaner. I edited your answer to include the full code to make this work, as there are several other changes that have to be made other than the one in `formatURL` – twiz Feb 18 '13 at 18:39
  • For some reason my edit to your answer got rejected. Not sure what the deal is. Anyway, I just posted my own answer with the code to fully implement the hashbang URL functionality. – twiz Feb 19 '13 at 00:03