Using URL fragment (#) for modal views in a pushState single-page app
backbone.js, javascript, pushstate, single-page-application, url-routing
Solution
If I understand your problem correctly, I don't believe you need a hash and I do believe you can implement the modal by simply adding more routes. If you need to support a login modal over other existing routes, simply add another route that adds login to the end. Here would be an example:
var Login = Backbone.Model.extend({
closeLogin: function () {
this.set({ open: false }, { silent: true });
this.trigger('loginClosed');
}
});
var LoginView = Backbone.View.extend({
el: '#loginModal',
initialize: function () {
this.listenTo(this.model, 'loginClosed', this.closeLogin);
},
closeLogin: function () {
// do whatever you would normally do to close the modal
}
});
var Router = Backbone.Router.extend({
routes: {
'/login': 'defaultRouteLogin',
'': 'defaultRoute',
'otherpage/login': 'otherPageRouteLogin',
'otherpage': 'otherPageRoute'
},
defaultRoute: function () {
// defaultRoute behaviors
this.login.closeLogin();
},
defaultRouteLogin: function () {
// defaultRoute behavior
// instantiate login modal
},
otherPageRoute: function () {
// otherPageRoute behavior
this.login.closeLogin();
},
otherPageRouteLogin: function () {
// otherPageRoute behavior
// instantiate login modal
},
initialize: function () {
this.login = new Login();
this.loginView = new LoginView({ model: this.login });
}
});
To support back-button behavior, use a method that closes your login modal on every route where you could open your modal. Be sure to order the routes correctly.
Problem
We're using `Backbone.Router` in a pushstate-only mode, so all our client routes are hash-less. However, we're facing a difficulty with implementing modal views in our app. The challenge is the following: - We want Back button to hide the current modal view (so we need it to have a URL); - We want Forward to show it again without redrawing the whole app; - We want to be able to show modals “on top of” any existing route and not just on one page; - We want to be able to make links that immediately show a specific modal (such as login modal view). In other words, we want modal views to be represented in history. Our first attempt was to use URL like `/login` for login modal and handle them specifically in `route` handler. When we're on `/otherpage`, opening modal would navigate to `/login`, and, when modal is closed, navigate again to `/otherpage`. However, this has a very major problem: URL like `/login` doesn't “know” over which view it should be drawn, so we have to redraw everything when pressing Back and Forward. This actually makes sense: Login modal over Home screen should have different URL from Login modal over Other Page. My second thought was that maybe we can use hashes for indicating current modal view: ``` / /#login /otherpage /otherpage#login ``` This makes the routing handler simple: - First, draw the actual views based on matched route, just like we did before. - After that, if hash is present, display a modal view on top. This also fits with the idea of hash being a “fragment” of the visible document. (Yes, apps are not documents, bla bla bla. We still want them to be addressable.) Are there any inherent problems in this approach? Is there a better approach satisfying our conditions?