Skip to content

Commit 27fae29

Browse files
crisbetojelbourn
authored andcommitted
fix(youtube-player): avoid setInterval change detection when player is created (#17894)
When we create a `Player` object for the `youtube-player` component, the YouTube API kicks off a 250ms `setInterval` which will trigger change detection even if the video is passive on the page. These change run the initialization code outside the `NgZone` so that doesn't happen.
1 parent 81a204e commit 27fae29

File tree

1 file changed

+10
-4
lines changed

1 file changed

+10
-4
lines changed

src/youtube-player/youtube-player.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import {
3838
OperatorFunction,
3939
pipe,
4040
Subject,
41+
of,
4142
} from 'rxjs';
4243

4344
import {
@@ -210,7 +211,7 @@ export class YouTubePlayer implements AfterViewInit, OnDestroy, OnInit {
210211
const endSecondsObs = this._endSeconds.pipe(startWith(undefined));
211212
const suggestedQualityObs = this._suggestedQuality.pipe(startWith(undefined));
212213

213-
/** An observable of the currently loaded player. */
214+
// An observable of the currently loaded player.
214215
const playerObs =
215216
createPlayerObservable(
216217
this._youtubeContainer,
@@ -219,6 +220,7 @@ export class YouTubePlayer implements AfterViewInit, OnDestroy, OnInit {
219220
widthObs,
220221
heightObs,
221222
this.createEventsBoundInZone(),
223+
this._ngZone
222224
).pipe(waitUntilReady(), takeUntil(this._destroyed), publish());
223225

224226
// Set up side effects to bind inputs to the player.
@@ -480,6 +482,7 @@ function createPlayerObservable(
480482
widthObs: Observable<number>,
481483
heightObs: Observable<number>,
482484
events: YT.Events,
485+
ngZone: NgZone
483486
): Observable<UninitializedPlayer | undefined> {
484487

485488
const playerOptions =
@@ -489,7 +492,7 @@ function createPlayerObservable(
489492
map(([videoId, [width, height]]) => videoId ? ({videoId, width, height, events}) : undefined),
490493
);
491494

492-
return combineLatest([youtubeContainer, playerOptions])
495+
return combineLatest([youtubeContainer, playerOptions, of(ngZone)])
493496
.pipe(
494497
skipUntilRememberLatest(iframeApiAvailableObs),
495498
scan(syncPlayerState, undefined),
@@ -507,7 +510,7 @@ function skipUntilRememberLatest<T>(notifier: Observable<boolean>): MonoTypeOper
507510
/** Destroy the player if there are no options, or create the player if there are options. */
508511
function syncPlayerState(
509512
player: UninitializedPlayer | undefined,
510-
[container, videoOptions]: [HTMLElement, YT.PlayerOptions | undefined],
513+
[container, videoOptions, ngZone]: [HTMLElement, YT.PlayerOptions | undefined, NgZone],
511514
): UninitializedPlayer | undefined {
512515
if (!videoOptions) {
513516
if (player) {
@@ -519,7 +522,10 @@ function syncPlayerState(
519522
return player;
520523
}
521524

522-
const newPlayer: UninitializedPlayer = new YT.Player(container, videoOptions);
525+
// Important! We need to create the Player object outside of the `NgZone`, because it kicks
526+
// off a 250ms setInterval which will continually trigger change detection if we don't.
527+
const newPlayer: UninitializedPlayer =
528+
ngZone.runOutsideAngular(() => new YT.Player(container, videoOptions));
523529
// Bind videoId for future use.
524530
newPlayer.videoId = videoOptions.videoId;
525531
return newPlayer;

0 commit comments

Comments
 (0)