-
Notifications
You must be signed in to change notification settings - Fork 27.4k
feat($compile): add $onDestroy directive lifecycle hook #14127
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -296,6 +296,8 @@ | |
* * `$onInit` - Called on each controller after all the controllers on an element have been constructed and | ||
* had their bindings initialized (and before the pre & post linking functions for the directives on | ||
* this element). This is a good place to put initialization code for your controller. | ||
* * `$onDestroy` - Called when the controller's scope has been destroyed. This is a good place to put | ||
* code for tearing down your controller and for example removing event listeners. | ||
* | ||
* #### `require` | ||
* Require another directive and inject its controller as the fourth argument to the linking function. The | ||
|
@@ -2424,6 +2426,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { | |
if (isFunction(controller.instance.$onInit)) { | ||
controller.instance.$onInit(); | ||
} | ||
if (isFunction(controller.instance.$onDestroy)) { | ||
scope.$on('$destroy', controller.instance.$onDestroy); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Two main comments here (was actually implementing this just now until I saw this):
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for your input. I fixed you second point. |
||
} | ||
}); | ||
|
||
// PRELINKING | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4584,6 +4584,7 @@ describe('$compile', function() { | |
"class Foo {\n" + | ||
" constructor($scope) {}\n" + | ||
" $onInit() { this.check(); }\n" + | ||
" $onDestroy() {}\n" + | ||
" check() {\n" + | ||
" expect(this.data).toEqualData({\n" + | ||
" 'foo': 'bar',\n" + | ||
|
@@ -4599,6 +4600,7 @@ describe('$compile', function() { | |
" }\n" + | ||
"}"); | ||
spyOn(Controller.prototype, '$onInit').andCallThrough(); | ||
spyOn(Controller.prototype, '$onDestroy').andCallThrough(); | ||
|
||
module(function($compileProvider) { | ||
$compileProvider.directive('fooDir', valueFn({ | ||
|
@@ -4625,7 +4627,11 @@ describe('$compile', function() { | |
'dir-str="Hello, {{whom}}!" ' + | ||
'dir-fn="fn()"></div>')($rootScope); | ||
expect(Controller.prototype.$onInit).toHaveBeenCalled(); | ||
expect(Controller.prototype.$onDestroy).not.toHaveBeenCalled(); | ||
expect(controllerCalled).toBe(true); | ||
$rootScope.$destroy(); | ||
expect(Controller.prototype.$onDestroy).toHaveBeenCalled(); | ||
|
||
}); | ||
/*jshint +W061 */ | ||
}); | ||
|
@@ -5351,6 +5357,29 @@ describe('$compile', function() { | |
}); | ||
}); | ||
|
||
it('should call `controller.$onDestroy`, if provided when the controllers is destroyed', function() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. controllers --> controller |
||
|
||
function Controller1($element) { this.id = 1; this.element = $element; } | ||
Controller1.prototype.$onDestroy = function() {}; | ||
spyOn(Controller1.prototype, '$onDestroy').andCallThrough(); | ||
|
||
function Controller2($element) { this.id = 2; this.element = $element; } | ||
Controller2.prototype.$onDestroy = function() {}; | ||
spyOn(Controller2.prototype, '$onDestroy').andCallThrough(); | ||
|
||
angular.module('my', []) | ||
.directive('d1', valueFn({ controller: Controller1 })) | ||
.directive('d2', valueFn({ controller: Controller2 })); | ||
|
||
module('my'); | ||
inject(function($compile, $rootScope) { | ||
element = $compile('<div d1 d2></div>')($rootScope); | ||
$rootScope.$destroy(); | ||
expect(Controller1.prototype.$onDestroy).toHaveBeenCalledOnce(); | ||
expect(Controller2.prototype.$onDestroy).toHaveBeenCalledOnce(); | ||
}); | ||
}); | ||
|
||
describe('should not overwrite @-bound property each digest when not present', function() { | ||
it('when creating new scope', function() { | ||
module(function($compileProvider) { | ||
|
@@ -5741,6 +5770,8 @@ describe('$compile', function() { | |
siblingController = this.friend; | ||
}; | ||
spyOn(MeController.prototype, '$onInit').andCallThrough(); | ||
MeController.prototype.$onDestroy = function() {}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Place this line right under |
||
spyOn(MeController.prototype, '$onDestroy').andCallThrough(); | ||
|
||
angular.module('my', []) | ||
.directive('me', function() { | ||
|
@@ -5770,8 +5801,12 @@ describe('$compile', function() { | |
inject(function($compile, $rootScope, meDirective) { | ||
element = $compile('<parent><me sibling></me></parent>')($rootScope); | ||
expect(MeController.prototype.$onInit).toHaveBeenCalled(); | ||
expect(MeController.prototype.$onDestroy).not.toHaveBeenCalled(); | ||
expect(parentController).toEqual(jasmine.any(ParentController)); | ||
expect(siblingController).toEqual(jasmine.any(SiblingController)); | ||
$rootScope.$destroy(); | ||
expect(MeController.prototype.$onDestroy).toHaveBeenCalled(); | ||
|
||
}); | ||
}); | ||
|
||
|
@@ -5788,6 +5823,9 @@ describe('$compile', function() { | |
}; | ||
spyOn(MeController.prototype, '$onInit').andCallThrough(); | ||
|
||
MeController.prototype.$onDestroy = function() {}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Place this line right under |
||
spyOn(MeController.prototype, '$onDestroy').andCallThrough(); | ||
|
||
angular.module('my', []) | ||
.directive('me', function() { | ||
return { | ||
|
@@ -5814,8 +5852,12 @@ describe('$compile', function() { | |
inject(function($compile, $rootScope, meDirective) { | ||
element = $compile('<parent><me sibling></me></parent>')($rootScope); | ||
expect(MeController.prototype.$onInit).toHaveBeenCalled(); | ||
expect(MeController.prototype.$onDestroy).not.toHaveBeenCalled(); | ||
expect(parentController).toBeUndefined(); | ||
expect(siblingController).toBeUndefined(); | ||
$rootScope.$destroy(); | ||
expect(MeController.prototype.$onDestroy).toHaveBeenCalled(); | ||
|
||
}); | ||
}); | ||
|
||
|
@@ -5830,9 +5872,12 @@ describe('$compile', function() { | |
$onInit: function() { | ||
parentController = this.container; | ||
siblingController = this.friend; | ||
} | ||
}, | ||
$onDestroy: function() {} | ||
}; | ||
spyOn(meController, '$onInit').andCallThrough(); | ||
spyOn(meController, '$onDestroy').andCallThrough(); | ||
|
||
return meController; | ||
} | ||
|
||
|
@@ -5864,8 +5909,11 @@ describe('$compile', function() { | |
inject(function($compile, $rootScope, meDirective) { | ||
element = $compile('<parent><me sibling></me></parent>')($rootScope); | ||
expect(meController.$onInit).toHaveBeenCalled(); | ||
expect(meController.$onDestroy).not.toHaveBeenCalled(); | ||
expect(parentController).toEqual(jasmine.any(ParentController)); | ||
expect(siblingController).toEqual(jasmine.any(SiblingController)); | ||
$rootScope.$destroy(); | ||
expect(meController.$onDestroy).toHaveBeenCalled(); | ||
}); | ||
}); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should also be explicit about when this is called wrt to child component/directives.
(And have tests for it 😃)