Troubleshooting Problems with Angular 8 Routing and the YouTube IFrame API

Currently, I am in the process of integrating the YouTube IFrame API into my application to deliver course videos to enrolled users. The setup allows me to provide my Angular application with a YouTube embed code, and the IFrame API successfully loads the video upon the initial loading of the course page (as discussed in another post on StackOverflow). However, an issue arises when I navigate using router.navigate to another component and return to the course component - the video and IFrame fail to load.

My assumption is that the document object is created when the page first loads, granting access to the onYouTubeIframeAPIReady method. Once I switch routes, it seems like the previous document object is no longer available.

video.component.ts:

    ngAfterViewInit() {
        const doc = (<any>window).document;
        let playerApiScript = doc.createElement('script');
        playerApiScript.type = 'text/javascript';
        playerApiScript.src = 'https://www.youtube.com/iframe_api';
        doc.body.appendChild(playerApiScript);
    }

    ngOnInit() {
        (<any>window).onYouTubeIframeAPIReady = () => {
            console.log('Ready');
            this.player = new (<any>window).YT.Player('player', {
                height: '100%',
                width: '100%',
                videoId: 'Bey4XXJAqS8',
                playerVars: {'autoplay': 0, 'rel': 0, 'controls': 2},
                events: {
                    'onReady': () => {
                    },
                    'onStateChange': () => {
                    }
                }
            });
        };
    }

video.component.html

<div class="embed-responsive embed-responsive-16by9">
    <div id="player" class="embed-responsive-item"></div>
</div>

I aim for the video to either re-render or maintain its rendering even if the user navigates to a different component. Please feel free to ask for more information if necessary!

Answer №1

import {Component, OnInit} from '@angular/core';

@Component({
  selector: 'art-youtube-video',
  templateUrl: './youtube-video-dialog.component.html',
  styleUrls: ['./youtube-video-dialog.component.scss']
})
export class YoutubeVideoDialogComponent implements OnInit {
  public YTPlayer: any;
  public videoDetails: any;
  public videoPlayer: any;
  
  constructor() { }

  initializeYoutubeAPI() {
    var tag = document.createElement('script');
    tag.src = 'https://www.youtube.com/iframe_api';
    var firstScriptTag = document.getElementsByTagName('script')[0];
    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
  }

  ngOnInit() {
    this.initializeYoutubeAPI();
    this.videoDetails = '1cH2cerUpMQ' //video id

    window['onYouTubeIframeAPIReady'] = (event) => {
      this.YTPlayer = window['YT'];
      this.reframed = false;
      this.videoPlayer = new window['YT'].Player('player', {
        videoId: this.videoDetails,
        events: {
          'onStateChange': this.onPlayerStateChange.bind(this),
          'onError': this.onPlayerError.bind(this),
          'onReady': (event) => {
            event.target.playVideo();
          }
        }
      });
    };
  }

  stopVideoPlayback() {
    this.videoPlayer.stopVideo();
  }

  onPlayerStateChange(event) {
    switch (event.data) {
      case window['YT'].PlayerState.PLAYING:
        if (this.getVideoTime() == 0) {
          console.log('started at time ' + this.getVideoTime());
        } else {
          console.log('playing at time ' + this.getVideoTime())
        };
        break;
      case window['YT'].PlayerState.PAUSED:
        if (this.videoPlayer.getDuration() - this.videoPlayer.getCurrentTime() != 0) {
          console.log('paused at ' + this.getVideoTime());
        };
        break;
      case window['YT'].PlayerState.ENDED:
        console.log('video ended');
        break;
    };
  };

  //utility function
  getVideoTime() {
    return Math.round(this.videoPlayer.getCurrentTime())
  };

  onPlayerError(event) {
    switch (event.data) {
      case 2:
        console.log('Error loading video: ' + this.videoDetails)
        break;
      case 100:
        break;
      case 101 || 150:
        break;
    };
  };

}

Answer №2

Here is a sample YouTube link: https://www.youtube.com/watch?v=11AXUzhXK4E&t=2s
 <iframe
  src="https://www.youtube.com/embed/11AXUzhXK4E"
  frameborder="0"  width="800"  height="800"  allowfullscreen>
</iframe>

Answer №3

The reason behind this is that once the YouTube script is loaded into the Angular app, it does not reinitialize the script when we navigate back to the component. To solve this issue, I removed the two scripts in the ngOnDestroy hook and reloaded the script when the component is reinitialized.

const tag = document.createElement('script');
tag.src = 'https://www.youtube.com/iframe_api';
tag.id = 'iframe-api-script';

const tag2 = document.createElement('script');
tag2.src = 'https://www.youtube.com/s/player/7acefd5d/www-widgetapi.vflset/www-widgetapi.js';
tag2.id = 'www-widgetapi-script';

const firstScriptTag = document.getElementsByTagName('script')[0];
if (firstScriptTag && firstScriptTag.parentNode) {
   firstScriptTag.parentNode.insertBefore(tag2, firstScriptTag);
   firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
}

...

ngOnDestroy() {
    document.getElementById('www-widgetapi-script')?.remove();
    document.getElementById('iframe-api-script')?.remove();
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Does the Rxjs timer cease functioning when the user switches to a different window?

I have encountered an issue while setting a 60-second countdown with rxjs timer on Angular 11. The countdown works properly as long as the window is kept open. However, if I switch to another tab in the same browser, the timer stops after a few seconds. Th ...

What causes overflow-x scroll to prevent scrolling to the left unless the initial scroll is to the left?

<div class="row justify-content-center mob-view" > <div class="col-lg-3 col-md-4" *ngFor="let slide of specialization"> <div class="brower-box"> &l ...

What is the solution to toggling the openAll() or closeAll() functionality for an Angular Material expansion panel button?

Is there a way for me to toggle between two buttons: OpenAll and CloseAll? Can I determine the state of mat-accordion, whether it is fully opened or closed, using a boolean value? <div class="row"> <mat-icon *ngIf="accordion.op ...

Exploring the Possibilities of Retrieving an Array within an Object in Angular 2

After retrieving this array from my database : https://i.sstatic.net/OKrCH.png This function is used to fetch the data : getMembers(){ this.db.list(`projects/${this.auth.userId}/${this.uploadService.pro.name}/teams/`).snapshotChanges().pipe( map( ...

How to declare multiple components in @ngModule using Angular 2

Currently, I'm working on a hefty project that combines Angular 2 with ASP.net MVC. I've got around 120 components declared within the @NgModule block like so: @NgModule({ imports: [CommonModule], declarations: [Component1, Component2, Comp ...

angular2-mdl encountered a 404 error and could not be located

I have encountered a strange 404 error stating that the page is not found. Despite installing angular2-mdl using npm install angular2-mdl --save and confirming its presence in the node_modules directory, the error persists. Below is a snippet from my app. ...

Dynamic value for href in div using Angular

Implementing a dynamic submenu using Angular is my current project. At the moment, I have set the href attribute with hardcoding as shown below: <ng-template #preSelectionMenuItem let-preSelections="preSelections"> <div class=' ...

Angular 6: Issue with displaying data on the user interface

Hello! I am attempting to fetch and display a single data entry by ID from an API. Here is the current setup: API GET Method: app.get('/movies/:id', (req, res) => { const id = req.params.id; request('https://api.themoviedb.org/ ...

Learning how to implement the "as" syntax in TypeScript

Currently tackling an angular project that is functioning flawlessly, yet encountering a linting test failure. Unfortunately, the information provided in this post did not offer much assistance. The error message I'm facing reads as follows: ERROR: C ...

Having trouble transferring sound files to Cloudinary API through javascript

I have successfully implemented a function in my React Native application to upload images to Cloudinary. Now, I am trying to modify the function to upload audio files as well. Despite specifying the "resource_type" parameter as "raw", "video", or "auto", ...

Angular Observables do not update local variables when making API calls

For some reason, I cannot set a value in my local variable as expected. Here is the code snippet: export class memberComponent implements OnInit { member : Member = new Member(); constructor(private memberService: MemberService) {} ngOnInit() { ...

Using Angular 6 HttpClient to retrieve an object of a specific class

Previously, we were able to validate objects returned from http api calls using the instanceof keyword in Angular. However, with the introduction of the new HttpClient Module, this method no longer works. I have tried various simple methods, but the type c ...

Tips for arranging, categorizing, and separating the array in alphabetical order using Angular 2+

I am looking to organize the buttons' content alphabetically into groups A-H, I-Q, and R-Z. The code I'm using is in Angular2+. https://i.sstatic.net/pPCBO.png My array: this.dropdownList = [ { item_text: 'Organisation' }, ...

"An error occurred while parsing: Unexpected token" was encountered when the class field was added

Behold, the TypeScript code below has been successfully compiled with Webpack (this implies that static class fields and other TypeScript features are supported): export default class ConfigRepresentative { constructor() { console.log('ok&apos ...

Error in Angular8: Attempting to loop through an undefined property

I have searched tirelessly for a solution, but none of them seem to work. Every time I click on the edit button, it redirects me to edit-event page and shows me this error: ERROR TypeError: Cannot read property 'categories' of undefined n ...

The SCRIPT1002 error message popped up, signaling a syntax issue between IE11 and Angular

Encountering an error when trying to open my Angular project in IE11. The error message is as follows: SCRIPT1002: Syntax error vendor.js (224520,1) The line causing the error (224520) looks like this: class PreventableEvent { constructor() { ...

Must run the angular code in a sequential order

I need to run the code in a specific order; first the foreach loop should be executed, followed by a call to the getHistory() method. Your assistance is greatly appreciated. const execute = async()=>{ await this.currentContent.forEach(async ...

Oops! Running the SPA development server using the command 'npm start' encountered an issue and couldn't start

placeholder textCurrently, I am working on an Angular project within VS 2022. However, upon running it, I encountered the following error: Error Message: Unable to initiate the SPA development server using command 'npm start'. Details: There see ...

A guide on simulating mouse events in Angular for testing Directives

I am currently exploring the functionality of a column resizable directive that relies on mouse events such as mouseup, mousemove, and mousedown. resize-column.directive.ts import { Directive, OnInit, Renderer2, Input, ElementRef, HostListener } from "@a ...

Issue: The observer's callback function is not being triggered when utilizing the rxjs interval

Here is a method that I am using: export class PeriodicData { public checkForSthPeriodically(): Subscription { return Observable.interval(10000) .subscribe(() => { console.log('I AM CHECKING'); this.getData(); }); } ...