calling function when entering a route - ember.js

In my application, I have the standard parent/children routes relationship. I want to execute a function every time the user clicks (via {{link-to}}) into a new route. I've tried implementing this function in the parent and children routes' activate hook, but the function is called only when the application first starts, and not called during subsequent route transition.
Is there a way to tell Ember to execute a function every time the user transitions into a new route?

There is a willTransition action, that is called when a transition is made. You can use the following to get all transitions:
App.ApplicationRoute = Ember.Route.extend({
actions: {
willTransition: function(transition) {
console.log('Transitioning to', transition.targetName);
}
}
});
This is a fiddle with this in action http://jsfiddle.net/marciojunior/Cs7S6/

Related

How to run an action on every route transition in ember?

I have an Ember application with several routes and I want to scroll the page to top every time the user makes a transition. The problem is that I don't want to duplicate the same code on every route's didTransitionmethod.
So, Is there a way that I can observe all routes transitions and execute (declare) an action in just one place ?
Another way to put put this: If I have a parent route, can I fire the same action to all of its children ?
My current solution is to fire the action on every route's didTransition method like this:
//router.js
Router.map(function() {
this.route('login');
this.route('portal',{ path:'/'}, function() {
this.route('clientes');
this.route('convite');
this.route('usuario', function() {
this.route('view', { path: '/:id' });
});
});
//everyChildren.route.js
didTransition(){
this.super(...arguments);
Ember.$("html, body").animate({ scrollTop: 0 }, "slow");
}
Is there a way to do this ?
PS:. I am using Ember version 2.14.0
You can try to define didTransition action on application route. If child route does not define handler for event, it should bubble up to parent route and application is a most top route.
Alternatively, you can create a mixin with didTransition action, and add that mixin to certain routes.
Also, looking at your code I think you might be interested in using liquid-fire addon (if your goal is animated transitions)
Use ember-router-scroll.
This addon does what you want

Ember observer / run schedule for each time a route is loaded

I have a property in an Ember controller which has an observer. This is called when I first visit the page / route and also when I change the a dropdown that the value is bound to (as expected).
What isn't happening, is the observer being fired when I re-visit the page and the value is re-set (to the same value it was when I left the page).
The reason I need this, is that the value is used as a filter for another function and that function is called inside the observer.
Is there a way to make an observer fire even if the value is the same as it was before?
Or alternatively, a sensible way to fire a function (perhaps via the run loop) from a controller, each time the controller is loaded / route visited?
In setupController route hook, you will get controller reference so you can use that to call observer function.
setupController(controller,model){
this._super(...arguments);
controller.get('testObserver')();
}
I prefer below style to observe a property.
setupController(controller, model){
if (this.get('somelogic')) {
controller.addObserver('yourProperty', controller, this.get('callback'));
}
}
resetController(controller, isExisting, transition) {
if (isExisting) {
controller.removeObserver('yourProperty', controller, this.get('callback'));
}
}

Ember.js Route: How to avoid infinite loop when refreshing parent route from child setupController

I have two nested routes, projects and projects/project. Whenever I open a project, I would like the projects route to be refreshed.
What I am doing know is to call a controller function during the setupController hook (that will end up calling refresh in the projects route). Something like this:
// project route
setupController: function(controller, model) {
this.controllerFor('projects').send('search');
controller.set('model', model)
}
// projects controller
actions: {
search: function() {
// do other things
this.send('refreshModel');
}
}
// projects route
actions: {
refreshModel: function() {
this.refresh();
}
}
The problem is that, when the refresh function is called, it not only refreshes the current route, but also all the children routes, meaning that this setupController hook will be executed again, creating this infinite loop.
Is there a way to make Ember refresh just the current route, ignoring its children?
Thank you.
To answer directly, there is no way to only refresh the parent route. You might, however, be able to just reload the model and still get the behaviour you need.
See the relevant API documentation for DS.Model#reload.

Ember.js route hook to be called on every transition

Is there a route hook in Ember.js that is called on every transition, even if the new route is the same as the old route (for example, clicking a top-level navigation link to the same route).
I tried activate, but it's only being called once, and is not being called again when I use the top-level navigation to go to the same route I'm already in.
Example jsFiddle: When I click "Test" the first time, the activate hook is called, but when I click it a second time, it does not.
You can setup a didTransition in the router, exactly how Ember does it for Google Analytics.
App.Router.reopen({
doSomething: function() {
// do something here
return;
}.on('didTransition')
});
See example here: http://emberjs.com/guides/cookbook/helpers_and_components/adding_google_analytics_tracking/
UPDATE: (new link since old is broken)
https://guides.emberjs.com/v1.10.0/cookbook/helpers_and_components/adding_google_analytics_tracking/
Activate is not being called a second time because This hook is executed when the router enters the route... And when you click on that link-to a second time, the router isn't doing anything... As in, no transition is being made (although it is "attempted").
http://emberjs.com/api/classes/Ember.Route.html#method_activate
The method that I have found to work best is observing the currentPath from within a controller. I use this for animations between routes.
In your application controller you can do something like the following:
currentPathChange: function () {
switch(this.get('currentPath')){
case 'test.index':
this.doSomething();
break;
case 'test.new':
this.doSomethingElse();
break;
}
}.observes('currentPath')
You should be able to access almost any part of your app from the application controller, so it's a nice "root hook," I suppose.
Example: http://jsfiddle.net/mattblancarte/jxWjh/2/
Did you already consider the hook willTransition?
http://emberjs.com/guides/routing/preventing-and-retrying-transitions/
App.SomeRoute = Ember.Route.extend({
actions: {
willTransition: function(transition) {
// do your stuff
}
}
});
Alter/Hack EmberJS code and add a jQuery event trigger inside the doTransition() Function. This is Best but kind of defeating the point.
As of today, 1 year later and Ember 2.0 kind of out, there is NO OTHER WAY :(
Ember does not provide a way to track route-change attempts! This includes URLattemts(history), link-to attempts, hash change attempts etc..

Make a programmatically Created Controller Delegate (Bubble) Events

I have an app with many similar views which I instantiate programmatically to "DRY-up" my app.
The problem is that controllers instantiated programmatically do not delegate actions in the actions hash further. This is clear because there is nothing from which the controller can derive the hierarchy. There should be a way, however, to tell a controller which parent controller it has to use for event bubbling. Does anyone know it?
You shouldn't be initializing controller's on your own. All controller initialization should be handled by Ember itself. Another interesting note, controller's are intended to be singletons in the application. The only exception to this being the itemController when looping over an ArrayController. You can read more about it in the guides. Quote from the guides:
In Ember.js applications, you will always specify your controllers as
classes, and the framework is responsible for instantiating them and
providing them to your templates.
This makes it super-simple to test your controllers, and ensures that
your entire application shares a single instance of each controller.
Update 1:
An example of how to do routing for a wizard:
App.Router.map(function() {
this.resource('wizard', function() {
this.route('step1');
this.route('step2');
this.route('step3');
});
});
This way, you can have a separate controller/view/template per step of the wizard. If you have logic around how much of each step should be completed prior to transitioning to the next one, you can handle that in the individual routes.
Update 2:
In the event that the number of steps aren't predetermined, but are based on the data being fed to the app, you can make a WizardController that is an ArrayController where each item in the array is a step in the wizard. Then, use the lookupItemController hook on the ArrayController, kind of like this:
App.WizardRoute = Ember.Route.extend({
model: function() {
return [
{controllerName: 'step1', templateName: 'step1'},
{controllerName: 'step2', templateName: 'step2'},
{controllerName: 'step3', templateName: 'step3'}
];
}
});
App.WizardController = Ember.ArrayController.extend({
lookupItemController: function(modelObject) {
return modelObject.controllerName;
}
});
{{#each step in controller}}
{{view Ember.View templateName=step.templateName}}
{{/each}}
As another, probably better, alternative, you can override the renderTemplate hook in the route where you're pulling down the model for the next step in the wizard and pass in the appropriate templateName and controller in the render call, kind of like you see here.
Point being, I think it should be possible to do this without having to instantiate controllers yourself.