mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(core): viewport trigger deregistering callbacks multiple times (#52115)
Adds a check to the viewport cleanup function to prevent it from re-processing elements that have been fully cleaned up, because it can lead to the `IntersectionObserver` being destroyed even though there are still pending triggers. This can happen, because we have cleanup callbacks both for the block is loaded, but also when the placeholder view is destroyed. Fixes #52113. PR Close #52115
This commit is contained in:
committed by
Andrew Scott
parent
861ce3a7c5
commit
d5dad3eb4c
@@ -176,6 +176,11 @@ class DeferIntersectionManager {
|
||||
entry.callbacks.add(callback);
|
||||
|
||||
return () => {
|
||||
// It's possible that a different cleanup callback fully removed this element already.
|
||||
if (!this.viewportTriggers.has(trigger)) {
|
||||
return;
|
||||
}
|
||||
|
||||
entry!.callbacks.delete(callback);
|
||||
|
||||
if (entry!.callbacks.size === 0) {
|
||||
|
||||
@@ -3642,5 +3642,44 @@ describe('@defer', () => {
|
||||
|
||||
expect(loadingFnInvokedTimes).toBe(1);
|
||||
}));
|
||||
|
||||
it('should load deferred content in a loop', fakeAsync(() => {
|
||||
@Component({
|
||||
standalone: true,
|
||||
template: `
|
||||
@for (item of items; track item) {
|
||||
@defer (on viewport) {d{{item}} }
|
||||
@placeholder {<button>p{{item}} </button>}
|
||||
}
|
||||
`
|
||||
})
|
||||
class MyCmp {
|
||||
items = [1, 2, 3, 4, 5, 6];
|
||||
}
|
||||
|
||||
const fixture = TestBed.createComponent(MyCmp);
|
||||
fixture.detectChanges();
|
||||
const buttons = Array.from<Element>(fixture.nativeElement.querySelectorAll('button'));
|
||||
const items = fixture.componentInstance.items;
|
||||
|
||||
// None of the blocks are loaded yet.
|
||||
expect(fixture.nativeElement.textContent.trim()).toBe('p1 p2 p3 p4 p5 p6');
|
||||
|
||||
// First half of the blocks is loaded.
|
||||
for (let i = 0; i < items.length / 2; i++) {
|
||||
MockIntersectionObserver.invokeCallbacksForElement(buttons[i], true);
|
||||
fixture.detectChanges();
|
||||
flush();
|
||||
}
|
||||
expect(fixture.nativeElement.textContent.trim()).toBe('d1 d2 d3 p4 p5 p6');
|
||||
|
||||
// Second half of the blocks is loaded.
|
||||
for (let i = items.length / 2; i < items.length; i++) {
|
||||
MockIntersectionObserver.invokeCallbacksForElement(buttons[i], true);
|
||||
fixture.detectChanges();
|
||||
flush();
|
||||
}
|
||||
expect(fixture.nativeElement.textContent.trim()).toBe('d1 d2 d3 d4 d5 d6');
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user