Skip to content

Commit

Permalink
fix(youtube-player): avoid setInterval change detection when player i…
Browse files Browse the repository at this point in the history
…s 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.
  • Loading branch information
crisbeto authored and jelbourn committed Dec 6, 2019
1 parent 81a204e commit 27fae29
Showing 1 changed file with 10 additions and 4 deletions.
14 changes: 10 additions & 4 deletions src/youtube-player/youtube-player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
OperatorFunction,
pipe,
Subject,
of,
} from 'rxjs';

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

/** An observable of the currently loaded player. */
// An observable of the currently loaded player.
const playerObs =
createPlayerObservable(
this._youtubeContainer,
Expand All @@ -219,6 +220,7 @@ export class YouTubePlayer implements AfterViewInit, OnDestroy, OnInit {
widthObs,
heightObs,
this.createEventsBoundInZone(),
this._ngZone
).pipe(waitUntilReady(), takeUntil(this._destroyed), publish());

// Set up side effects to bind inputs to the player.
Expand Down Expand Up @@ -480,6 +482,7 @@ function createPlayerObservable(
widthObs: Observable<number>,
heightObs: Observable<number>,
events: YT.Events,
ngZone: NgZone
): Observable<UninitializedPlayer | undefined> {

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

return combineLatest([youtubeContainer, playerOptions])
return combineLatest([youtubeContainer, playerOptions, of(ngZone)])
.pipe(
skipUntilRememberLatest(iframeApiAvailableObs),
scan(syncPlayerState, undefined),
Expand All @@ -507,7 +510,7 @@ function skipUntilRememberLatest<T>(notifier: Observable<boolean>): MonoTypeOper
/** Destroy the player if there are no options, or create the player if there are options. */
function syncPlayerState(
player: UninitializedPlayer | undefined,
[container, videoOptions]: [HTMLElement, YT.PlayerOptions | undefined],
[container, videoOptions, ngZone]: [HTMLElement, YT.PlayerOptions | undefined, NgZone],
): UninitializedPlayer | undefined {
if (!videoOptions) {
if (player) {
Expand All @@ -519,7 +522,10 @@ function syncPlayerState(
return player;
}

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

0 comments on commit 27fae29

Please sign in to comment.