Skip to content

feat(core): re-introduce nested leave animations scoped to component boundaries#67647

Merged
mattrbeck merged 1 commit intoangular:mainfrom
thePunderWoman:nested-animations-redux
Mar 13, 2026
Merged

feat(core): re-introduce nested leave animations scoped to component boundaries#67647
mattrbeck merged 1 commit intoangular:mainfrom
thePunderWoman:nested-animations-redux

Conversation

@thePunderWoman
Copy link
Contributor

This commit re-introduces support for nested leave animations with a critical adjustment to prevent cross-component blocking. Wait for nested inner animate.leave transitions natively only when they exist within the same component's view or its embedded tracking structures (like @if and @for).

This resolves the issue where route navigations and parental destruction would excessively stall by traversing down into child component architectures to wait for their distinct leaf animations.

BREAKING CHANGE: Leave animations are no longer limited to the element being removed.

Fixes #67633

@thePunderWoman thePunderWoman added action: review The PR is still awaiting reviews from at least one requested reviewer area: core Issues related to the framework runtime target: major This PR is targeted for the next major release labels Mar 12, 2026
@ngbot ngbot bot added this to the Backlog milestone Mar 12, 2026
@angular-robot angular-robot bot added detected: breaking change PR contains a commit with a breaking change detected: feature PR contains a feature commit labels Mar 12, 2026
@thePunderWoman thePunderWoman force-pushed the nested-animations-redux branch from f39855a to 67c38af Compare March 12, 2026 17:52
…boundaries

This commit re-introduces support for nested leave animations with a critical adjustment to prevent cross-component blocking. Wait for nested inner `animate.leave` transitions natively only when they exist within the same component's view or its embedded tracking structures (like `@if` and `@for`).

This resolves the issue where route navigations and parental destruction would excessively stall by traversing down into child component architectures to wait for their distinct leaf animations.

BREAKING CHANGE: Leave animations are no longer limited to the element being removed.

Fixes angular#67633
@thePunderWoman thePunderWoman force-pushed the nested-animations-redux branch from c6d85c8 to 72b6269 Compare March 12, 2026 23:03
Copy link
Contributor

@atscott atscott left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review of PR 67647: Nested leave animations

This PR re-introduces support for nested leave animations but stops traversal at component boundaries to prevent excessive blocking during route navigations and parental destruction.

Here is an analysis of how this PR interacts with content projection and transplanted views, and whether the behavior makes sense:

1. Content Projection (<ng-content>)

Behavior:
Projected nodes physically reside within the child component's DOM, but their TNodes and logical state belong to the parent component's LView (where the content was declared).
Because the PR explicitly avoids traversing into component views (isComponentHost check was removed) and instead only traverses LContainers within the current view hierarchy:

  • If the child component destroys the <ng-content> projection insertion point (e.g., via an @if inside the child component), the child component will not wait for leave animations on the projected nodes. It has no visibility into the parent's LView where those animations are registered.
  • If the parent component hides the <child-component> (e.g., via an @if around the child), the parent's LView executes leave animations. The projected nodes' leave animations will be collected and waited for, because they are tracked directly in the parent's LView's animations.leave map.

Conclusion: Makes Sense 👍
This behavior is consistent with Angular's mental model. Animations defined in a component's template belong to that component. If a component is destroyed, it should wait for its own animations. If a child component simply hides projected content, it shouldn't be blocked by animations defined in the parent's context without the parent's knowledge.

2. Transplanted Views (e.g., *ngTemplateOutlet or structural directives)

Behavior:
Transplanted views are created from a TemplateRef defined in one component but inserted into an LContainer within another component.
The PR implements collectNestedViewAnimations as follows:

if (tNode.type & TNodeType.AnyContainer) {
  const lContainer = lView[tNode.index];
  if (isLContainer(lContainer)) {
    for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
      const subView = lContainer[i] as LView;
      if (subView[TVIEW].type === TViewType.Embedded) { // <-- The key check
        collectAllViewLeaveAnimations(subView, collectedPromises);
      }
    }
  }
}

Because transplanted views are instances of TViewType.Embedded residing inside an LContainer, they will be traversed and their leave animations will be collected and waited for by the component that is hosting the insertion point.

Interestingly, if you use ViewContainerRef.createComponent() to dynamically insert a full component into an LContainer, its subView[TVIEW].type is TViewType.Root (not Embedded). Therefore, dynamically inserted components will not be waited for, which perfectly aligns with the PR's goal of not blocking on component boundaries!

Conclusion: Makes Sense 👍
If a component hosts a dynamic embedded view (even if the template was defined elsewhere), that view is acting as a granular piece of UI within the host component. Waiting for its leave animations before destruction is the correct and expected behavior. At the same time, maintaining the boundary for dynamically inserted components keeps the performance and blocking characteristics consistent with statically declared components.

Summary

The logic in aggregateDescendantAnimations and collectNestedViewAnimations thoughtfully bounds the wait time to the current component's logical DOM structures (including embedded views and transplanted templates) while successfully avoiding the trap of waiting for child component architectures. The behavior for both content projection and transplanted views is logically sound.

@thePunderWoman thePunderWoman removed the request for review from JeanMeche March 12, 2026 23:15
@thePunderWoman thePunderWoman added action: merge The PR is ready for merge by the caretaker merge: caretaker note Alert the caretaker performing the merge to check the PR for an out of normal action needed or note and removed action: review The PR is still awaiting reviews from at least one requested reviewer labels Mar 12, 2026
@thePunderWoman
Copy link
Contributor Author

Caretaker note: Presubmit is green after deflake. Safe to merge.

@mattrbeck mattrbeck merged commit df659b8 into angular:main Mar 13, 2026
21 of 23 checks passed
@mattrbeck
Copy link
Member

This PR was merged into the repository. The changes were merged into the following branches:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action: merge The PR is ready for merge by the caretaker area: core Issues related to the framework runtime detected: breaking change PR contains a commit with a breaking change detected: feature PR contains a feature commit merge: caretaker note Alert the caretaker performing the merge to check the PR for an out of normal action needed or note target: major This PR is targeted for the next major release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Scoped Child leave animations

3 participants