fix(animations): do not leak DOM nodes/styling for host triggered animations (#18853)
Closes #18606 PR Close #18853
This commit is contained in:
parent
fd701b07f0
commit
fcadeb2079
|
@ -837,7 +837,7 @@ export class TransitionAnimationEngine {
|
|||
}
|
||||
|
||||
const allLeaveNodes: any[] = [];
|
||||
const leaveNodesWithoutAnimations: any[] = [];
|
||||
const leaveNodesWithoutAnimations = new Set<any>();
|
||||
for (let i = 0; i < this.collectedLeaveElements.length; i++) {
|
||||
const element = this.collectedLeaveElements[i];
|
||||
const details = element[REMOVAL_FLAG] as ElementAnimationState;
|
||||
|
@ -845,7 +845,7 @@ export class TransitionAnimationEngine {
|
|||
addClass(element, LEAVE_CLASSNAME);
|
||||
allLeaveNodes.push(element);
|
||||
if (!details.hasAnimation) {
|
||||
leaveNodesWithoutAnimations.push(element);
|
||||
leaveNodesWithoutAnimations.add(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -937,12 +937,14 @@ export class TransitionAnimationEngine {
|
|||
}
|
||||
|
||||
// these can only be detected here since we have a map of all the elements
|
||||
// that have animations attached to them...
|
||||
const enterNodesWithoutAnimations: any[] = [];
|
||||
// that have animations attached to them... We use a set here in the event
|
||||
// multiple enter captures on the same element were caught in different
|
||||
// renderer namespaces (e.g. when a @trigger was on a host binding that had *ngIf)
|
||||
const enterNodesWithoutAnimations = new Set<any>();
|
||||
for (let i = 0; i < allEnterNodes.length; i++) {
|
||||
const element = allEnterNodes[i];
|
||||
if (!subTimelines.has(element)) {
|
||||
enterNodesWithoutAnimations.push(element);
|
||||
enterNodesWithoutAnimations.add(element);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1435,9 +1437,11 @@ function cloakElement(element: any, value?: string) {
|
|||
}
|
||||
|
||||
function cloakAndComputeStyles(
|
||||
driver: AnimationDriver, elements: any[], elementPropsMap: Map<any, Set<string>>,
|
||||
driver: AnimationDriver, elements: Set<any>, elementPropsMap: Map<any, Set<string>>,
|
||||
defaultStyle: string): [Map<any, ɵStyleData>, any[]] {
|
||||
const cloakVals = elements.map(element => cloakElement(element));
|
||||
const cloakVals: string[] = [];
|
||||
elements.forEach(element => cloakVals.push(cloakElement(element)));
|
||||
|
||||
const valuesMap = new Map<any, ɵStyleData>();
|
||||
const failedElements: any[] = [];
|
||||
|
||||
|
@ -1456,7 +1460,10 @@ function cloakAndComputeStyles(
|
|||
valuesMap.set(element, styles);
|
||||
});
|
||||
|
||||
elements.forEach((element, i) => cloakElement(element, cloakVals[i]));
|
||||
// we use a index variable here since Set.forEach(a, i) does not return
|
||||
// an index value for the closure (but instead just the value)
|
||||
let i = 0;
|
||||
elements.forEach(element => cloakElement(element, cloakVals[i++]));
|
||||
return [valuesMap, failedElements];
|
||||
}
|
||||
|
||||
|
|
|
@ -733,6 +733,46 @@ export function main() {
|
|||
flushMicrotasks();
|
||||
expect(fixture.debugElement.nativeElement.children.length).toBe(0);
|
||||
}));
|
||||
|
||||
it('should properly evaluate pre/auto-style values when components are inserted/removed which contain host animations',
|
||||
fakeAsync(() => {
|
||||
@Component({
|
||||
selector: 'parent-cmp',
|
||||
template: `
|
||||
<child-cmp *ngFor="let item of items"></child-cmp>
|
||||
`
|
||||
})
|
||||
class ParentCmp {
|
||||
items: any[] = [1, 2, 3, 4, 5];
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'child-cmp',
|
||||
template: '... child ...',
|
||||
animations:
|
||||
[trigger('host', [transition(':leave', [animate(1000, style({opacity: 0}))])])]
|
||||
})
|
||||
class ChildCmp {
|
||||
@HostBinding('@host') public hostAnimation = 'a';
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]});
|
||||
|
||||
const engine = TestBed.get(ɵAnimationEngine);
|
||||
const fixture = TestBed.createComponent(ParentCmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
const element = fixture.nativeElement;
|
||||
fixture.detectChanges();
|
||||
|
||||
cmp.items = [0, 2, 4, 6]; // 1,3,5 get removed
|
||||
fixture.detectChanges();
|
||||
|
||||
const items = element.querySelectorAll('child-cmp');
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
expect(item.style['display']).toBeFalsy();
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
it('should cancel and merge in mid-animation styles into the follow-up animation, but only for animation keyframes that start right away',
|
||||
|
|
Loading…
Reference in New Issue