@@ -14,6 +14,9 @@ import {
14
14
EnvironmentInjector ,
15
15
NgZone ,
16
16
afterNextRender ,
17
+ afterRender ,
18
+ untracked ,
19
+ AfterRenderRef ,
17
20
} from '@angular/core' ;
18
21
import { Location } from '@angular/common' ;
19
22
import { Observable , Subject , merge , SubscriptionLike , Subscription } from 'rxjs' ;
@@ -60,6 +63,10 @@ export class OverlayRef implements PortalOutlet {
60
63
/** Stream of mouse outside events dispatched to this overlay. */
61
64
readonly _outsidePointerEvents = new Subject < MouseEvent > ( ) ;
62
65
66
+ private _renders = new Subject < void > ( ) ;
67
+
68
+ private _afterRenderRef : AfterRenderRef ;
69
+
63
70
constructor (
64
71
private _portalOutlet : PortalOutlet ,
65
72
private _host : HTMLElement ,
@@ -79,6 +86,18 @@ export class OverlayRef implements PortalOutlet {
79
86
}
80
87
81
88
this . _positionStrategy = _config . positionStrategy ;
89
+
90
+ // Users could open the overlay from an `effect`, in which case we need to
91
+ // run the `afterRender` as `untracked`. We don't recommend that users do
92
+ // this, but we also don't want to break users who are doing it.
93
+ this . _afterRenderRef = untracked ( ( ) =>
94
+ afterRender (
95
+ ( ) => {
96
+ this . _renders . next ( ) ;
97
+ } ,
98
+ { injector : this . _injector } ,
99
+ ) ,
100
+ ) ;
82
101
}
83
102
84
103
/** The overlay's HTML element */
@@ -223,7 +242,7 @@ export class OverlayRef implements PortalOutlet {
223
242
224
243
// Keeping the host element in the DOM can cause scroll jank, because it still gets
225
244
// rendered, even though it's transparent and unclickable which is why we remove it.
226
- this . _detachContentWhenStable ( ) ;
245
+ this . _detachContentWhenEmpty ( ) ;
227
246
this . _locationChanges . unsubscribe ( ) ;
228
247
this . _outsideClickDispatcher . remove ( this ) ;
229
248
return detachmentResult ;
@@ -256,6 +275,8 @@ export class OverlayRef implements PortalOutlet {
256
275
}
257
276
258
277
this . _detachments . complete ( ) ;
278
+ this . _afterRenderRef . destroy ( ) ;
279
+ this . _renders . complete ( ) ;
259
280
}
260
281
261
282
/** Whether the overlay has attached content. */
@@ -491,15 +512,15 @@ export class OverlayRef implements PortalOutlet {
491
512
}
492
513
493
514
/** Detaches the overlay content next time the zone stabilizes. */
494
- private _detachContentWhenStable ( ) {
515
+ private _detachContentWhenEmpty ( ) {
495
516
// Normally we wouldn't have to explicitly run this outside the `NgZone`, however
496
517
// if the consumer is using `zone-patch-rxjs`, the `Subscription.unsubscribe` call will
497
518
// be patched to run inside the zone, which will throw us into an infinite loop.
498
519
this . _ngZone . runOutsideAngular ( ( ) => {
499
520
// We can't remove the host here immediately, because the overlay pane's content
500
521
// might still be animating. This stream helps us avoid interrupting the animation
501
522
// by waiting for the pane to become empty.
502
- const subscription = this . _ngZone . onStable
523
+ const subscription = this . _renders
503
524
. pipe ( takeUntil ( merge ( this . _attachments , this . _detachments ) ) )
504
525
. subscribe ( ( ) => {
505
526
// Needs a couple of checks for the pane and host, because
0 commit comments