Extend default handling with Ember.Route.reopen


#1

Hey everyone,

I started to create my first Ember app a and I have a problem with Ember.Route.reopen. I want to extend the beforeModel function to all my routs for some kind of session handling. My plan was like this:

Ember.Route.reopen({
	beforeModel: function(transition){
		hasSession ? routTo X : routeTo login
	// will not be called if direct access
	},
	afterModel: function(transition){
		hasSession ? routTo X : routeTo login
	// will always be called
	},
}

This works perfect when I click around the App, however, when I directly access the path in the browser, he does not call the extended beforeModel if the Route has a dedicated beforeModel.

App.PlaygroundRoute = Ember.Route.extend({
	beforeModel: function(transition) {
		// will be called
	},
	afterModel: function(){
		// will be called
	}	
});

What I do not understand, it works when I put it in the afterModel. Then he doesn’t care if a dedicated afterModel function is set in the Route.

Did I miss something here?

(Sorry for my bad English, it’s not my mother tongue)

*Edit for layout


#2

I tried something different with Mixins:

App.SecureRoute = Ember.Mixin.create({
	beforeModel: function(transition) {
		console.log("beforeModel in the Mixin");
	},
	afterModel: function(model, transition) {
		console.log("afterModel in the Mixin");
	}
});

and then this in my playground route

App.PlaygroundRoute = Ember.Route.extend(App.SecureRoute, {
	beforeModel: function(transition) {
		console.log("beforeModel in the route");
	},
	afterModel: function(model, transition){
		console.log("afterModel in the route");		
	}
});

The mixin is never called. What is the problem here? (Obviously the guy in front of the computer)


#3

I found the solution but I do not understand it.

I added playground as route to my router map. After I changed it to a resource the Mixin and the Extension (reopen) both work. Somebody care to explain?


#4

The problem you’re facing is that when you override the method (ie. define a method in a child class with the same name as in the parent), calling it will just execute the function you provided. In order to execute also the implementation from parent you may use this._super method - it will call a method with the same name as current method from the superclass:


Ember.Route.reopen({
  beforeModel: function(transition) {
    console.log('beforeModel in Ember.Route');
  }
});

App.PlaygroundRoute = Ember.Route.extend({
  beforeModel: function(transition) {
    this._super(transition);
    console.log('beforeModel in PlaygroundRoute');
  }
});

You can also use a little trick if you don’t want to specify all arguments in super:

this._super.apply(this, arguments);

This will call the method from the superclass with all of the arguments passed to the current method.

There is also one thing that you could improve here, reopening Ember.Route is not necessary. If you define App.Route, it will be used for all of the generated routes automatically. So you could do:

App.Route = Ember.Route.extend();

App.PlaygroundRoute = App.Route.extend();

#5

This is a bit of a coincidence. The reason of that behaviour is because routes and resources are nested, so when you have a resource inside map, you will end up with 2 routes: App.ApplicationRoute and the resource route (like in your case App.PlaygroundRoute). So in your case, when you use resource, beforeModel will be fired on ApplicationRoute first and then overrided beforeModel will be called from the App.PlaygroundRoute.

If you create an App.ApplicationRoute class with beforeModel overrided as well, you will see that model from reopened Ember.Route class will not be called:

Ember.Route.reopen({
  beforeModel: function(transition) {
    console.log('beforeModel in Ember.Route');
  }
});

App.ApplicationRoute = Ember.Route.extend({
  beforeModel: function(transition) {
    console.log('beforeModel in ApplicationRoute');
  }
});

App.PlaygroundRoute = Ember.Route.extend({
  beforeModel: function(transition) {
    console.log('beforeModel in PlaygroundRoute');
  }
});

Such routes will not output console.log from Ember.Route as long as you don’t use _super


#6

Thank you, dorgus. I understand that know. The reason why it didn’t worked in the first example was indeed a empty beforeModel method I had in my ApplicationRoute.

What would be a good solution to make a check each time a Route is called/opened without adding this._super(transition) in each of my Routs to come? I just want to be super save and make 100% sure that this is called.


#7

I know this is an old thread but…

@drogus - Can you elaborate on

There is also one thing that you could improve here, reopening Ember.Route is not necessary. If you define App.Route, it will be used for all of the generated routes automatically. So you could do:

App.Route = Ember.Route.extend();

App.PlaygroundRoute = App.Route.extend();

Are you saying that if I were to define App.Route() I could then extend it for all other routes or that by defining it, Ember will automatically extend it.

I am trying to accomplish a similar goal but don’t want to have to remember to extend App.Route all of the time.

Thanks