(
routerEvent: NavigationEnd | NavigationSkipped,
anchor: string | null,
)
| 126 | } |
| 127 | |
| 128 | private scheduleScrollEvent( |
| 129 | routerEvent: NavigationEnd | NavigationSkipped, |
| 130 | anchor: string | null, |
| 131 | ): void { |
| 132 | if (this.isHydrating) return; |
| 133 | const scroll = untracked(this.transitions.currentNavigation)?.extras.scroll; |
| 134 | this.zone.runOutsideAngular(async () => { |
| 135 | // The scroll event needs to be delayed until after change detection. Otherwise, we may |
| 136 | // attempt to restore the scroll position before the router outlet has fully rendered the |
| 137 | // component by executing its update block of the template function. |
| 138 | // |
| 139 | // #57109 (we need to wait at least a macrotask before scrolling. AfterNextRender resolves in microtask event loop with Zones) |
| 140 | // We could consider _also_ waiting for a render promise though one should have already happened or been scheduled by this point |
| 141 | // and should definitely happen before rAF/setTimeout. |
| 142 | // #53985 (cannot rely solely on setTimeout because a frame may paint before the timeout) |
| 143 | await new Promise((resolve) => { |
| 144 | setTimeout(resolve); |
| 145 | if (typeof requestAnimationFrame !== 'undefined') { |
| 146 | requestAnimationFrame(resolve); |
| 147 | } |
| 148 | }); |
| 149 | this.zone.run(() => { |
| 150 | this.transitions.events.next( |
| 151 | new Scroll( |
| 152 | routerEvent, |
| 153 | this.lastSource === 'popstate' ? this.store[this.restoredId] : null, |
| 154 | anchor, |
| 155 | scroll, |
| 156 | ), |
| 157 | ); |
| 158 | }); |
| 159 | }); |
| 160 | } |
| 161 | |
| 162 | /** @docs-private */ |
| 163 | ngOnDestroy(): void { |
no test coverage detected