feat($compile): add $onDestroy directive lifecycle hook#14127
feat($compile): add $onDestroy directive lifecycle hook#14127bobbijvoet wants to merge 3 commits intoangular:masterfrom
Conversation
Introduce a destroy hook for directives which follows the ng2 component lifecycle Closes angular#14020
src/ng/compile.js
Outdated
| controller.instance.$onInit(); | ||
| } | ||
| if (isFunction(controller.instance.$onDestroy)) { | ||
| scope.$on('$destroy', controller.instance.$onDestroy); |
There was a problem hiding this comment.
Two main comments here (was actually implementing this just now until I saw this):
- This doesn't allow late-bound
$onDestroymethods to be added. If it's not there at the time of controller instantiation, the lifecycle hook will not function. Does that follow expectations, or do we need to be checking this at the time of destruction? - The
thisbinding in the$onDestroymethod is going to be incorrect here due to the manner in which you're passing$onDestroy. I see that you copied the unit tests from the one above it as I had done, but removed thecheckcall. You should re-introduce that portion of the test since it would have failed on this point
There was a problem hiding this comment.
Thanks for your input. I fixed you second point.
Bind $onDestroy handler to the controller instance Closes angular#14020
|
@dcherman Yes, we should allow late-bound $onDestroy methods. We could attach scope.$on('$destroy') listeners for every controller and then check if the $onDestroy method is there. But it does not feel right to attach this listener on every controller. What do you think? |
src/ng/compile.js
Outdated
| controller.instance.$onInit(); | ||
| } | ||
| if (isFunction(controller.instance.$onDestroy)) { | ||
| scope.$on('$destroy', controller.instance.$onDestroy.bind(controller.instance)); |
There was a problem hiding this comment.
The problem with this is that scope might be a parent scope that is not directly related to the element.
Maybe we could use $element.on('$destroy', ...) instead (I haven't given it much thought, though).
There was a problem hiding this comment.
Ok. The element.$on solution sounds better to me because we care about the destruction of the component/directive in specific, not about the scope. Thanks for the insight, will create a new solution.
Call $onDestroy when element is removed, possibility to set $onDestroy method after controller initialization Closes angular#14020
| controller.instance.$onInit(); | ||
| } | ||
|
|
||
| $element.on('$destroy', function() { |
There was a problem hiding this comment.
The $destroy event won't function properly when you have a transclude: "element" directive since events are not fired on the comment node that becomes $element.
There was a problem hiding this comment.
So it should be implemented using $scope.$on('$destroy', ... ?
|
Figuring out exactly where to run this logic is trickier than it looks. We only have two existing hooks that can be used for something like this which are the |
|
Edit: This idea is pretty much invalid/derp. The new scope's lifecycle would match the parent's, making it pointless to have created it in the first place. How common do we feel that So I was thinking about this for a little bit this morning, and although the
I have no data to back up this assertion (at all), but I feel like it may not be that common to have a controller on a directive that doesn't define either an isolate or new scope, so this codepath may be hit less frequently than we might imagine. The new |
|
The more I think about it, the more it doesn't make sense to try to cover all corner-cases and end up with a complicated bloat (that will have little resemblance to ng2). Since the main purpose (or at least one of the main purposes) of the directive lifecycle hooks is to enable structuring your directive in a way that is close to the ng2-way (and thus facilitate the migration), it is reasonable to only offer the In ng2 all directives have sort of their own scope/context (the component/directive class). So, by not providing the This way, we will avoid lots of irrelevant corner cases and we'll be able to implement it using WDYT ? /cc @petebacondarwin |
|
I start to think that may be components are not directives:
So, may be hooks can only be applied from component constructor or only to components. In such case no need for forEach controller, less overhead in directives, ... (may be all of this can be achieved by providing pre/post-link functions just for components). |
|
I think we should implement it via |
|
What I mean about components "are not" directives is may be that something such simple as changing return {
controller: controller,
controllerAs: identifierForController(options.controller) || options.controllerAs || '$ctrl',
template: makeInjectable(template),
templateUrl: makeInjectable(options.templateUrl),
transclude: options.transclude,
scope: {},
bindToController: options.bindings || {},
restrict: 'E',
require: !options.require && [name] || [name].concat(options.require),
link: {
pre: function(scope, element, attrs, controllers) {
if (isFunction(controllers[0].$onInit)) {
controllers[0].$onInit();
}
},
post: function(scope, element, attrs, controllers) {
scope.$on('$destroy', function() {
if (isFunction(controllers[0].$onDestroy)) {
controllers[0].$onDestroy();
}
});
// placed after registering to avoid not firing destroy if afterViewInit throws an exception (and save a non-optimizable try-catch)
if (isFunction(controllers[0].$afterViewInit)) {
controllers[0].$afterViewInit();
}
},
}; |
|
@drpicox - I like what you are saying but I feel that developers ought to expect the same hooks to be called even if they create component directives using the |
|
Yes, you are right. I was just trying to give another vision, but right, it is not the best one. |
This change adds in the following new lifecycle hooks, which map in some
way to those in Angular 2:
* `$onChanges(changesObj)` - Called whenever one-way bindings are updated. The `changesObj` is a hash whose keys
are the names of the bound properties that have changed, and the values are an object of the form
`{ currentValue: ..., previousValue: ... }`. Use this hook to trigger updates within a component such as
cloning the bound value to prevent accidental mutation of the outer value.
* `$onDestroy` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
external resources, watches and event handlers.
* `$afterViewInit` - Called after this controller's element and its children been linked. Similar to the post-link
function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
they are waiting for their template to load asynchronously and their own compilation and linking has been
suspended until that occurs.
Closes angular#14127
Closes angular#14030
Closes angular#14020
Closes angular#13991
This change adds in the following new lifecycle hooks, which map in some
way to those in Angular 2:
* `$onChanges(changesObj)` - Called whenever one-way bindings are updated. The `changesObj` is a hash whose keys
are the names of the bound properties that have changed, and the values are an object of the form
`{ currentValue: ..., previousValue: ... }`. Use this hook to trigger updates within a component such as
cloning the bound value to prevent accidental mutation of the outer value.
* `$onDestroy` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
external resources, watches and event handlers.
* `$afterViewInit` - Called after this controller's element and its children been linked. Similar to the post-link
function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
they are waiting for their template to load asynchronously and their own compilation and linking has been
suspended until that occurs.
Closes angular#14127
Closes angular#14030
Closes angular#14020
Closes angular#13991
Closes angular#14302
This change adds in the following new lifecycle hooks, which map in some
way to those in Angular 2:
* `$onChanges(changesObj)` - Called whenever one-way bindings are updated. The `changesObj` is a hash whose keys
are the names of the bound properties that have changed, and the values are an object of the form
`{ currentValue: ..., previousValue: ... }`. Use this hook to trigger updates within a component such as
cloning the bound value to prevent accidental mutation of the outer value.
* `$onDestroy` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
external resources, watches and event handlers.
* `$afterViewInit` - Called after this controller's element and its children been linked. Similar to the post-link
function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
they are waiting for their template to load asynchronously and their own compilation and linking has been
suspended until that occurs.
Closes angular#14127
Closes angular#14030
Closes angular#14020
Closes angular#13991
Closes angular#14302
This change adds in the following new lifecycle hooks, which map in some
way to those in Angular 2:
* `$onChanges(changesObj)` - Called whenever one-way bindings are updated. The `changesObj` is a hash whose keys
are the names of the bound properties that have changed, and the values are an object of the form
`{ currentValue: ..., previousValue: ... }`. Use this hook to trigger updates within a component such as
cloning the bound value to prevent accidental mutation of the outer value.
* `$onDestroy` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
external resources, watches and event handlers.
* `$afterViewInit` - Called after this controller's element and its children been linked. Similar to the post-link
function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
they are waiting for their template to load asynchronously and their own compilation and linking has been
suspended until that occurs.
Closes angular#14127
Closes angular#14030
Closes angular#14020
Closes angular#13991
Closes angular#14302
This change adds in the following new lifecycle hooks, which map in some
way to those in Angular 2:
* `$onChanges(changesObj)` - Called whenever one-way bindings are updated. The `changesObj` is a hash whose keys
are the names of the bound properties that have changed, and the values are an object of the form
`{ currentValue: ..., previousValue: ... }`. Use this hook to trigger updates within a component such as
cloning the bound value to prevent accidental mutation of the outer value.
* `$onDestroy` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
external resources, watches and event handlers.
* `$postLink` - Called after this controller's element and its children been linked. Similar to the post-link
function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
they are waiting for their template to load asynchronously and their own compilation and linking has been
suspended until that occurs.
Closes angular#14127
Closes angular#14030
Closes angular#14020
Closes angular#13991
Closes angular#14302
This change adds in the following new lifecycle hooks, which map in some
way to those in Angular 2:
* `$onChanges(changesObj)` - Called whenever one-way bindings are updated. The `changesObj` is a hash whose keys
are the names of the bound properties that have changed, and the values are an object of the form
`{ currentValue: ..., previousValue: ... }`. Use this hook to trigger updates within a component such as
cloning the bound value to prevent accidental mutation of the outer value.
* `$onDestroy` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
external resources, watches and event handlers.
* `$postLink` - Called after this controller's element and its children been linked. Similar to the post-link
function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
they are waiting for their template to load asynchronously and their own compilation and linking has been
suspended until that occurs.
Closes angular#14127
Closes angular#14030
Closes angular#14020
Closes angular#13991
Closes angular#14302
This change adds in the following new lifecycle hooks, which map in some
way to those in Angular 2:
* `$onChanges(changesObj)` - Called whenever one-way bindings are updated. The `changesObj` is a hash whose keys
are the names of the bound properties that have changed, and the values are an object of the form
`{ currentValue: ..., previousValue: ... }`. Use this hook to trigger updates within a component such as
cloning the bound value to prevent accidental mutation of the outer value.
* `$onDestroy` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
external resources, watches and event handlers.
* `$postLink` - Called after this controller's element and its children been linked. Similar to the post-link
function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
they are waiting for their template to load asynchronously and their own compilation and linking has been
suspended until that occurs.
Closes angular#14127
Closes angular#14030
Closes angular#14020
Closes angular#13991
Closes angular#14302
This change adds in the following new lifecycle hooks, which map in some
way to those in Angular 2:
* `$onChanges(changesObj)` - Called whenever one-way bindings are updated. The `changesObj` is a hash whose keys
are the names of the bound properties that have changed, and the values are an object of the form
`{ currentValue: ..., previousValue: ... }`. Use this hook to trigger updates within a component such as
cloning the bound value to prevent accidental mutation of the outer value.
* `$onDestroy` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
external resources, watches and event handlers.
* `$postLink` - Called after this controller's element and its children been linked. Similar to the post-link
function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
they are waiting for their template to load asynchronously and their own compilation and linking has been
suspended until that occurs.
Closes angular#14127
Closes angular#14030
Closes angular#14020
Closes angular#13991
Closes angular#14302
This change adds in the following new lifecycle hooks, which map in some
way to those in Angular 2:
* `$onChanges(changesObj)` - Called whenever one-way bindings are updated. The `changesObj` is a hash whose keys
are the names of the bound properties that have changed, and the values are an object of the form
`{ currentValue: ..., previousValue: ... }`. Use this hook to trigger updates within a component such as
cloning the bound value to prevent accidental mutation of the outer value.
* `$onDestroy` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
external resources, watches and event handlers.
* `$postLink` - Called after this controller's element and its children been linked. Similar to the post-link
function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
they are waiting for their template to load asynchronously and their own compilation and linking has been
suspended until that occurs.
Closes angular#14127
Closes angular#14030
Closes angular#14020
Closes angular#13991
Closes angular#14302
This change adds in the following new lifecycle hooks, which map in some
way to those in Angular 2:
* `$onChanges(changesObj)` - Called whenever one-way bindings are updated. The `changesObj` is a hash whose keys
are the names of the bound properties that have changed, and the values are an object of the form
`{ currentValue: ..., previousValue: ... }`. Use this hook to trigger updates within a component such as
cloning the bound value to prevent accidental mutation of the outer value.
* `$onDestroy` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
external resources, watches and event handlers.
* `$postLink` - Called after this controller's element and its children been linked. Similar to the post-link
function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
they are waiting for their template to load asynchronously and their own compilation and linking has been
suspended until that occurs.
Closes angular#14127
Closes angular#14030
Closes angular#14020
Closes angular#13991
Closes angular#14302
This change adds in the following new lifecycle hooks, which map in some
way to those in Angular 2:
* `$onChanges(changesObj)` - Called whenever one-way bindings are updated. The `changesObj` is a hash whose keys
are the names of the bound properties that have changed, and the values are an object of the form
`{ currentValue: ..., previousValue: ... }`. Use this hook to trigger updates within a component such as
cloning the bound value to prevent accidental mutation of the outer value.
* `$onDestroy` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
external resources, watches and event handlers.
* `$postLink` - Called after this controller's element and its children been linked. Similar to the post-link
function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
they are waiting for their template to load asynchronously and their own compilation and linking has been
suspended until that occurs.
Closes angular#14127
Closes angular#14030
Closes angular#14020
Closes angular#13991
Closes angular#14302
This change adds in the following new lifecycle hooks, which map in some
way to those in Angular 2:
* `$onChanges(changesObj)` - Called whenever one-way bindings are updated. The `changesObj` is a hash whose keys
are the names of the bound properties that have changed, and the values are an object of the form
`{ currentValue: ..., previousValue: ... }`. Use this hook to trigger updates within a component such as
cloning the bound value to prevent accidental mutation of the outer value.
* `$onDestroy` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
external resources, watches and event handlers.
* `$postLink` - Called after this controller's element and its children been linked. Similar to the post-link
function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
they are waiting for their template to load asynchronously and their own compilation and linking has been
suspended until that occurs.
Closes angular#14127
Closes angular#14030
Closes angular#14020
Closes angular#13991
Closes angular#14302
This change adds in the following new lifecycle hooks, which map in some
way to those in Angular 2:
* `$onChanges(changesObj)` - Called whenever one-way bindings are updated. The `changesObj` is a hash whose keys
are the names of the bound properties that have changed, and the values are an object of the form
`{ currentValue: ..., previousValue: ... }`. Use this hook to trigger updates within a component such as
cloning the bound value to prevent accidental mutation of the outer value.
* `$onDestroy` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
external resources, watches and event handlers.
* `$postLink` - Called after this controller's element and its children been linked. Similar to the post-link
function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
they are waiting for their template to load asynchronously and their own compilation and linking has been
suspended until that occurs.
Closes #14127
Closes #14030
Closes #14020
Closes #13991
Closes #14302
This PR introduces a new $onDestroy lifecycle hook for directives. This hook matches the destroy hook that's present in Angular 2.
Closes #14020