-
Notifications
You must be signed in to change notification settings - Fork 26.5k
feat(core): add enter and leave animation instructions #62682
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
843db01
to
46066a1
Compare
46066a1
to
ba72fa8
Compare
ba72fa8
to
594389f
Compare
3c38d4a
to
056dcb3
Compare
Deployed adev-preview for 342ed87 to: https://ng-dev-previews-fw--pr-angular-angular-62682-adev-prev-yvy73xs1.web.app Note: As new commits are pushed to this pull request, this link is updated after the preview is rebuilt. |
packages/core/src/animation.ts
Outdated
animate(el: Element, removeFn: Function): void { | ||
if (!this.outElements.has(el)) return; | ||
const details = this.outElements.get(el)!; | ||
const timeout = setTimeout(() => removeFn(), 4000); |
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.
4000 feels like a magic number here. Can we use a named a named constant and/or have a comment on why this value. (Would animations longer than 4s just get removed after ther 4s ?)
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.
Certainly. I've added a const and associated comment about why 4 seconds.
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.
Also to quickly answer your question here, if you're using an animation function (rather than classes) for animate.leave
, you are required to call the complete function to tell the framework when to remove the element. The feedback on the RFC was to have a timeout for safety, and so I went with the same timeout duration as cross document view transitions. So after a 4 second timeout, the element gets removed. If your animation is done before that, no issues.
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.
What if we can have an injection token that configures it? And the default would still be 4s.
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 can keep it like this for now, but we should probably output a dev-mode warning for cases when timeout is triggered (for ex. for cases when the "done" callback is never invoked). That can help to surface this situations and we can see if there are cases when the timeout needs to be adjusted.
A couple additional thoughts:
- This can be done in a followup PR
- We should consider cases when those warnings are produced for elements inside of
@for
loops and avoid spamming console with those warnings
72ef660
to
4d2fc63
Compare
packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json
Outdated
Show resolved
Hide resolved
277e2bf
to
e6569a6
Compare
/gemini review |
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.
Code Review
This pull request introduces support for animate.enter
and animate.leave
instructions for animations on nodes. The changes include new public APIs like AnimationCallbackEvent
, AnimationFunction
, and ANIMATIONS_DISABLED
, a new ElementRegistry
service to manage animations for leaving elements, and the core implementation in the rendering instructions. The changes are well-supported by a comprehensive suite of acceptance tests. I've identified a couple of potential issues in packages/core/src/animation.ts
and packages/core/src/render3/instructions/animation.ts
. Please see my detailed comments.
e6569a6
to
85f73c2
Compare
const tNode = getCurrentTNode()!; | ||
const nativeElement = getNativeByTNode(tNode, lView) as HTMLElement; | ||
|
||
if ((nativeElement as Node).nodeType !== Node.ELEMENT_NODE) { |
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.
Curious what'd be an example of such usage (I believe host bindings might be in that category)? If we come up with an example, we can see what'd be the best way to handle it and whether the code should be at compile-time (preferable, if possible) or at runtime.
packages/core/src/animation.ts
Outdated
animate(el: Element, removeFn: Function): void { | ||
if (!this.outElements.has(el)) return; | ||
const details = this.outElements.get(el)!; | ||
const timeout = setTimeout(() => removeFn(), 4000); |
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 can keep it like this for now, but we should probably output a dev-mode warning for cases when timeout is triggered (for ex. for cases when the "done" callback is never invoked). That can help to surface this situations and we can see if there are cases when the timeout needs to be adjusted.
A couple additional thoughts:
- This can be done in a followup PR
- We should consider cases when those warnings are produced for elements inside of
@for
loops and avoid spamming console with those warnings
This adds the instructions to support enter and leave animations on nodes.
3c026c8
to
8a926f5
Compare
9b3ded6
to
45211a4
Compare
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.
@thePunderWoman thanks for making the ElementRegistry
tree-shakable 👍
I've left a few minor comments and I also wanted to propose that we check whether we can use areAnimationSupported
to exit a bit earlier (e.g. in the main instructions vs inside of util functions later).
b1822ef
to
0d62304
Compare
// The animations will only be disabled in a test environment, and adding a microtask here | ||
// will allow the tests to be able to tick forward to resolve the next phase of animation | ||
// in their tests. | ||
Promise.resolve().then(() => { |
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.
I think this should run outside the Angular zone too, right? Unless the intent is to trigger change detection here for applications using NgZone. This would differ from zoneless applications in that case unless we create some trigger for those as well.
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.
This is actually test only. It's intended for people to be able to tick forward in tests and see the classes get added / removed. The token that disables animations is currently in the core/testing package and in a follow up will be only provided in the animations test utility. So it should be ok here.
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.
So to be fair, this will cause tests to behave differently than production (thanks NgZone...) by causing an extra change detection. For better or for worse (definitely for worse), we have observed tests trying to count CD cycles. There could be other cases that this affects as well. For example, the listener callbacks, which execute outside the Angular zone, may make changes to application state. If this isn't done with ngZone.run
, markForCheck
, etc. production code will not know to trigger change detection but the test code will due to the promise here.
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.
That's always going to be the case, unfortunately. getAnimations
does not exist on DOM nodes in a unit test environment. So no animations can actually happen in that case, meaning for those types of tests users have to disable animations. This at least gives them the option to see the classes being added and removed after a tick. So they can verify that the change at least happened. The acceptance tests for this are actually skipping the node
environment for that reason.
If you're running tests in a browser, you would not disable animations. So in that case, this code wouldn't be reachable, meaning animations would behave as normal.
0fb85a4
to
25a89cb
Compare
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.
LGTM, thanks @thePunderWoman 👍
Just left some minor comments, but none of them are blocking. Let's discuss the testing story as a followup.
packages/compiler-cli/src/ngtsc/annotations/component/src/animations.ts
Outdated
Show resolved
Hide resolved
if (animationsDisabled) { | ||
// add a microtask for test environments to be able to see classes | ||
// were added, then removed. | ||
Promise.resolve().then(() => { | ||
finalRemoveFn(); | ||
}); | ||
} |
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.
Not a blocker for this PR, but we'd probably need to discuss the testing story. I think that relying on the presence of classes may be a bit brittle (especially in case we need to emulate certain timing) and we may instead introduce a utility function to check if a given element was processed by the animation subsystem. That would also allow us to remove this special handling.
This creates a feature to detect usages of animate.leave and only enables the element removal registry when necessary
25a89cb
to
342ed87
Compare
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.
Reviewed-for: public-api
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.
reviewed-for: public-api
This PR was merged into the repository by commit 882522c. The changes were merged into the following branches: main |
This creates a feature to detect usages of animate.leave and only enables the element removal registry when necessary PR Close #62682
If `showHideRelatedCards` is refactored into native CSS, then the package `@angular/animations` could be dropped entirely.
This adds the instructions to support enter and leave animations on nodes.
Does this PR introduce a breaking change?