Ian
Ian

Reputation: 21

Ember Modal on a route accessible from anywhere

I'm building an app that has a user settings panel that pops up in a modal dialog. The panel should be accessible from any page in the app. I'm trying to figure out the best way to build this in Ember.

I would like to build it in such a way that when the app redirects to the "/settings" route, the modal dialog appears with the current route in the background as you would expect. Then when the modal is closed the app redirects back to that route.

If the user goes directly to "/settings" from her browser then the modal will appear with a default page in the background.

Here is what I have so far:

export default Ember.Route.extend({

  defaultParentRoute: "project.index",

  beforeModel: function(transition){
    // get the name of the current route or if
    // user came directly to this url, use default route name
    var parent = this.get("defaultParentRoute");
    var application = this.controllerFor('application');
    if(application && application.get('currentRouteName')) {
      parent = application.get('currentRouteName');
    }
    this.set("parentRoute", parent);
  },

  renderTemplate: function(){
    // make sure the current route is still rendered in the main outlet
    this.render(this.get("parentRoute"));

    // render this route into the 'modal' outlet
    this.render({
      into: 'application',
      outlet: 'modal'
    });
  },  

  actions: {
    removeModal: function(page){ 
      // go back to the previous route     
      this.transitionTo(this.get("parentRoute"));
    }
  }

});

This works pretty well when navigating to the the route from a link in the app. However if a user goes straight to "myapp/settings" in her browser then the default page template gets rendered but without any data or it tries to use the 'model' data from my settings route.

How do I make sure the template underneath the modal gets rendered with the appropriate data?

Here's a JS Bin to demonstrate. Try clicking on 'settings' from any page in the app, then refresh the browser while the settings modal is open.

Upvotes: 2

Views: 446

Answers (1)

Kevin Bullaughey
Kevin Bullaughey

Reputation: 2576

This organization seems a bit unnatural given Ember conventions. Generally the URL is supposed to represent a serialized version of state sufficient to reconstruct where the user was (see this old discussion).

It seems you want to put the modal state and the current route into the URL. It might be more natural for the settings modal panel to be accessible from other routes but to not change the URL, and then have another separate route which is dedicated to settings and shows only the settings.

Modal panels seem more like a drop-down menu, the opening and closing of which do not change the URL, even though they represent a minor state change.

If the reason you want to have the settings modal reflected in the URL is so that people can bookmark it or share the link, one option would be to have a permalink available on the settings page that gets them to the other dedicated route that is shareable.

The tricky bit with not having the settings as a route is that there is not an obvious place to load the model which behaves as nicely or works as easily as a route's model hook (i.e., which waits until the promise is resolved to complete the transition).

One way around this is to put functionality to load the settings model in a settings service, which can be injected anywhere:

SomeController = Ember.Controller.extend({
  settings: Ember.inject.service()
});

And have the settings service only show itself once the model has loaded.

SettingsService = Ember.Service.extend({
  settingsLoaded: false,
  ensureLoaded: function() {
    var _this = this;
    return new Ember.RSVP.Promise (resolve) {
      if (_this.get('settingsLoaded')) {
        resolve()
      } else {
        _this.store.find('settings', someId).then(function(model) {
          _this.set('model', model);
          _this.set('settingsLoaded', true);
          resolve();
        });
      }
    };
  }
});

Finally you can have a function on some controller that wants to show the settings modal only show it once the settings are loaded:

showSettings: function() {
  this.get('settings').ensureLoaded().then(function() {
    ... # something to show the modal pane.
  });
}

Upvotes: 1

Related Questions