Frage

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?

War es hilfreich?

Lösung

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.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top