feat($compile): add more lifecycle hooks to directive controllers#14302
feat($compile): add more lifecycle hooks to directive controllers#14302petebacondarwin merged 1 commit intoangular:masterfrom
Conversation
| 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. |
There was a problem hiding this comment.
Is it also how it happens now if you use regular postLink functions in module.directive-defined directives?
There was a problem hiding this comment.
Yes
I copied and pasted that text from the post link function :-)
There was a problem hiding this comment.
TBH, I feel that this last limitation makes this hook pretty usel...ehm...much less useful than the corresponding ng2 hook :(
I assume, with the current implementation, this hook is very similar to the post-link function, which means that:
(a) It doesn't offer new functionality for ng1 users.
(b) It doesn't help much with migrating but for very simple components with inline templates. Even caching the templates won't help, right ?
These are just assumptions atm (I haven't looked at the code yet), but if they are true, I'm a little skeptical about this hook.
There was a problem hiding this comment.
In my current project I avoid these problems by not using templateUrl but relying on Webpack being able to import HTML files using the ES6 import syntax. Unfortunately you can't do things like that without any preprocessing done so it can't be a general solution.
There was a problem hiding this comment.
You are right @gkalpak, which is why I was suggesting that we call it $afterLink instead.
It does, though, have value for components, where there is no way right now to hook into the post-link function.
There was a problem hiding this comment.
I am becoming very keen on the webpack template: require('some.template.html') approach :-)
There was a problem hiding this comment.
Yeah, I only wish TypeScript had something similar :(
But in the meantime, I believe most developers follow the "best practice" of caching their templates with $templateCache in a build step. Besides a component doesn't know what it's children (or its grandchildren) do wrt template vs templateUrl.
I am afraid this will confuse people and create some headaches while migrating.
If we can't/won't make it work similarly to ng2, it might be btter to name is $postLink and just provide it as a way to access the post-link function while using the .component() helper, as @petebacondarwin suggested.
|
I'd add tests that ensure |
|
Fixed the comment typo and added tests @mgol PTAL |
2afc7f3 to
5270c25
Compare
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
docs/content/guide/component.ngdoc
Outdated
| 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 |
There was a problem hiding this comment.
been linked --> have been linked
|
LGTM (once the typo @gkalpak noticed is fixed) |
src/ng/compile.js
Outdated
| * * `$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. | ||
| * * `$onChanges(changesObj)` - Called whenever one-way bindings are updated. The `changesObj` is a hash whose keys |
There was a problem hiding this comment.
Nitpick: I would put the < symbol there somewhere, because @ bindings are also called "one-way" in many blogsposts/tutorials and this might confuse users.
5270c25 to
8c4eef6
Compare
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
8c4eef6 to
715efb8
Compare
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
src/ng/compile.js
Outdated
|
|
||
| function triggerOnChangesHook() { | ||
| // We must run this hook in an apply since the $$postDigest runs outside apply | ||
| scope.$apply(function() { |
There was a problem hiding this comment.
Doesn't this mean there will be one extra digest for each controller (that has at least one changed binding) per digest ?
There was a problem hiding this comment.
Yes you are right.
The alternative is to use $applyAsync but this has the downside of delaying the call to $onChanges by maybe 100ms.
What is better?
There was a problem hiding this comment.
I think we could move the login that calls triggerOnChangeHook inside $compile (I mean make it a compiler-local thing) and store the changes for each controller there, so we only trigger one extra $digest.
|
I am also wondering if applying |
715efb8 to
cb864ce
Compare
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
cb864ce to
5450d24
Compare
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
docs/content/guide/component.ngdoc
Outdated
| } | ||
| ``` | ||
|
|
||
| - **Components have a well-defined lifecycle |
src/ng/compile.js
Outdated
| function flushOnChangesQueue() { | ||
| // We must run this hook in an apply since the $$postDigest runs outside apply | ||
| $rootScope.$apply(function() { | ||
| for (var i = 0, ii = onChangesQueue.length; i < ii; ++i) { |
There was a problem hiding this comment.
What about changes detected during this digest ?
We should either not hard code the length or collect them and run another digest (but then there's a risk for infinite loops).
There was a problem hiding this comment.
Good call...
All the items in the onChanges queue will be flushed before the new digest is triggered.
The sequence looks like:
- some change 1
- some change 2
- $digest
- schedule flushOnChangesQueue
- queue onChanges 1
- queue onChanges 2
- $$postDigest
- flushOnChangesQueue
- $apply
- trigger onChanges 1
- some change 1a
- trigger onChanges 2
- some change 2a
- $digest
- schedule onChangesFlush
- queue onChanges 1
- queue onChanges 2
- $$postDigest
- flushOnChangesQueue
- $apply
- trigger onChanges 1
- trigger onChanges 2
- $digest
- $apply
- flushOnChangesQueue
- trigger onChanges 1
- $apply
- flushOnChangesQueue
I think this means that we are OK (except for the potential for deep recursion). I'll make a test
There was a problem hiding this comment.
So, this looks good apart from the infinite loop issue.
Ideally, there should be some mechanism in place to prevent freezing the whole app, because someone misused $onChanges (a TTL-like mechanism maybe).
If not (e.g. it turns out to be too complicated), we should at least document very clearly the dangers of making changes that would trigger another $onChanges round. from inside an $onChanges() callback.
There was a problem hiding this comment.
It is not an infinite loop. It is an infinite recursion, which will trigger a RangeError exception that will prevent the browser from freezing and indicate to the developer that they have done a bad thing.
Added a test to demonstrate.
5450d24 to
1123985
Compare
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
1123985 to
2266b50
Compare
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
src/ng/compile.js
Outdated
| * * `$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. | ||
| * * `$onChanges(changesObj)` - Called whenever one-way (`<`) bindings are updated. The `changesObj` is a hash |
There was a problem hiding this comment.
Now we need to change this again to also mention @ 😁
2266b50 to
aca8559
Compare
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
|
LGTM with this comment (somehow) addressed |
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
2ac418e to
f60d0c4
Compare
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
|
I've added time-to-live constraints for onChanges hooks, which should address @gkalpak's last concern |
| <c1 prop="a" on-change="a = -a"></c1> | ||
| ``` | ||
|
|
||
| ```js |
There was a problem hiding this comment.
Can you add the component definition object? Makes it easier to understand if you know the bindings are bindings: {'prop': '<', onChange: '&'}
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
7e794d3 to
874c0fd
Compare
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
What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)
Feature
What is the current behavior? (You can also link to an open issue here)
Missing lifecycle hooks for directive controllers
What is the new behavior (if this is a feature change)?
Add these lifecycle hooks
Does this PR introduce a breaking change?
Nope
Please check if the PR fulfills these requirements
Other information:
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. ThechangesObjis a hash whose keysare 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 ascloning 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 releasingexternal resources, watches and event handlers.
$afterViewInit- Called after this controller's element and its children been linked. Similar to the post-linkfunction this hook can be used to set up DOM event handlers and do direct DOM manipulation.
Note that child elements that contain
templateUrldirectives will not have been compiled and linked sincethey 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