Having an issue with the $onDestroy of a Route controller not getting called.
class AccountController {
constructor($state, $scope, $rootScope) {
Object.assign(this, { $state, $scope, $rootScope });
this.watcher = $rootScope.$on('$stateChangeStart', (event, next, current) => {
this.currentNavItem = next.url.split('/')[1];
});
// IS CALLED.. but looks like memory keeps leaking and doesn't actually destory anything
$scope.$on('$destroy', () => {
this.watcher();
})
}
$onInit() {
console.log('init');
this.currentNavItem = null
this.currentNavItem = this.$state.current.url.split('/')[1];
}
// IS NOT CALLED
$onDestroy() {
console.log('destroy')
this.watcher();
}
}
AccountController.$inject = ['$state', '$scope', '$rootScope']
export default AccountController;
I can't seem to get the $onDestroy to ever fire.. if i remove the inject of scope that doesn't help either..
I am using angular 1.5.8, this happens on all browsers.
Below is the routes file that i'm using.
This issue happens on all children routes as well.
routes.$inject = ['$stateProvider'];
export default function routes($stateProvider) {
$stateProvider
.state('account', {
url: '/account',
template: require('./template.html'),
controller: 'AccountController',
controllerAs: 'account',
resolve: {}
}).state('account.you', {
url: '/you',
template: require('./tabs/you.html'),
controller: 'YouController',
controllerAs: 'you',
resolve: {}
}).state('account.users', {
url: '/users',
template: require('./tabs/users.html'),
controller: 'UsersController',
controllerAs: 'users',
resolve: {}
}).state('account.groups', {
url: '/groups',
template: require('./tabs/groups.html'),
controller: 'GroupsController',
controllerAs: 'groups',
resolve: {}
}).state('account.surveys', {
url: '/surveys',
template: require('./tabs/surveys.html'),
controller: 'SurveysController',
controllerAs: 'surveys',
resolve: {}
})
}
I can't see in the example where the controller actually gets destroyed, so it's hard to say where the problem is. Also note that $onDestroy is called when the containing scope is destroyed.
The controller is setup using controllersAs to avoid injecting scope in.. but when I do inject scope in destroy is called... to me that means the scope of the route is getting killed but not calling the controller $onDestroy.
This route is a main route on the global ui-view of the page. I simply navigate back and forth in these routes but nothing fires.
This looks like a ui-router
problem since $stateProvider
was mentioned here. You may want to ensure that they support the new hooks created in 1.5. I recall a similar issue filed a while back with $onInit
@dcherman $onInit and $onChanges all work and $onDestroy works in other controllers I have.. but on main routes it doesn't.. ui-router definitely supports it. but certain routes dont get called and I'm noticing just root routes. Routes that are top level vs child components and child routes.
Ill do some more investigating.
So i just did some refactoring and if i Route to a component vs a controller the $onDestroy works
so my state now looks like
export default angular.module('app', [uirouter])
.config(routing)
.component('accountComponent', AccountController)
.component("you", YouComponent)
.component("users", UsersComponent)
.component("groups", GroupsComponent)
.component("surveys", SurveysComponent)
.component("groupCreate", GroupCreateComponent)
.name;
now when i navigate to those components based on my stateprovider:
export default function routes($stateProvider) {
$stateProvider
.state('account', {
url: '/account',
template: "<account-component></account-component>",
component:"account",
resolve: {}
}).state('account.you', {
url: '/you',
template: "<you flex layout='column'></you>",
component: "you",
resolve: {}
}).state('account.users', {
url: '/users',
template: "<users flex layout='column'></users>",
component: "users",
resolve: {}
}).state('account.groups', {
url: '/groups',
template: "<groups flex layout='column'></groups>",
component: "groups",
resolve: {}
}).state('account.creategroup', {
url: '/groups/create',
template: "<group-create flex layout='column'></group-create>",
component: "groupCreate",
resolve: {}
}).state('account.surveys', {
url: '/surveys',
template: "<surveys flex layout='column'></surveys>",
component: "surveys",
resolve: {}
})
}
all the proper events fire..
Can you even reproduce the problem without ui-router?
This seems like a duplicate of #14376. There is a misconception that controller.$onDestroy()
is equivalent to $scope.$on('$destroy')
, where $scope
is the scope passed to the controller. This is not always the case.
More specifically, controller.$onDestroy()
is called when the scope where the controller "lives in" gets destroyed. When you have an isolate scope directive, the scope passed (as $scope
) to the controller is not the scope that the controller lives in, it is a child-scope that is created for the directive's template. Thus, calling $scope.$destroy()
will NOT destroy the scope that the controller lives in (and NOT call controller.$onDestroy()
).
The only case, where controller.$onDestroy()
is equivalent to $scope.$on('$destroy')
is when having a directive with scope: false
. This means that no new scope is created, so the scope passed (as $scope
) to the controller is the same scope that the controller lives in.
Closing since this is working as expected as far as Angular is concerned. If you think there is a bug with ui.router
, please submit an issue on their repo (possibly linking to this issue for context).
Most helpful comment
This seems like a duplicate of #14376. There is a misconception that
controller.$onDestroy()
is equivalent to$scope.$on('$destroy')
, where$scope
is the scope passed to the controller. This is not always the case.More specifically,
controller.$onDestroy()
is called when the scope where the controller "lives in" gets destroyed. When you have an isolate scope directive, the scope passed (as$scope
) to the controller is not the scope that the controller lives in, it is a child-scope that is created for the directive's template. Thus, calling$scope.$destroy()
will NOT destroy the scope that the controller lives in (and NOT callcontroller.$onDestroy()
).The only case, where
controller.$onDestroy()
is equivalent to$scope.$on('$destroy')
is when having a directive withscope: false
. This means that no new scope is created, so the scope passed (as$scope
) to the controller is the same scope that the controller lives in.Closing since this is working as expected as far as Angular is concerned. If you think there is a bug with
ui.router
, please submit an issue on their repo (possibly linking to this issue for context).