1

I've incorporated the code documented in the following article into a new component:

Play YouTube Videos Through Lightning Web Components

It seems like the two JS files referenced, iframe_api.js and widgetapi.js, don't always load when the component is first loaded. In other words, initially the embedded YouTube player craps out the very first time I load the component. Afterwards, the resources load fine. This is true even when I clear my Chrome cache--which seems odd to me since this would force my browser client to refetch the static resource from the org.

I thought that the use of the Promise.all() to encapsulate the two loadScript() calls would ensure that both JS resources are loaded before continuing, but in my experience, that is not the case.

renderedCallback() {
        .
        .
        .
        if (window.YT) {
            if (this.player) {
                this.player.cueVideoById(this.youTubeId);
            } else {
                this.onYouTubeIframeAPIReady();
            }
        } else {
            Promise.all([
                loadScript(this, YouTubePath + '/iframe_api.js'),
                loadScript(this, YouTubePath + '/widgetapi.js')
            ])
                .then(() => {
                    this.onYouTubeIframeAPIReady();
                })
                .catch(error => {
                    console.log('Error with loading the YouTube static resources');
                    console.log(JSON.stringify(error));
                    this.showErrorToast(error);
                });
        }
    }

I've tried splitting the two JS files into their own individual static resources, but the same problem happens.

Is there another way I can address this issue?

2
  • what does "craps out" mean?
    – Andy Ray
    Jul 22 '20 at 19:44
  • @AndyRay "craps out" = "does not load". Let me see if I can get the exact error message. Jul 22 '20 at 19:47
1

I'm not entirely sure why that method shouldn't work, but in your specific case, this playground should provide you with what you need. It's a component that plays YouTube videos given an ID.

Here's the relevant code:

import { LightningElement, api } from 'lwc';
import { loadScript } from 'lightning/platformResourceLoader';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { YT as YoutubePath1 } from './YouTubeJS/iframe_api.js';
import { YT as YouTubePath2 } from './YouTubeJS/widget_api.js';

export default class BasicYouTubePlayer extends LightningElement {
    @api youTubeId;
    player;

    renderedCallback() {
        if (!this.youTubeId) {
            return;
        }

        if (window.YT) {
            if (this.player) {
                this.player.cueVideoById(this.youTubeId);
            } else {
                this.onYouTubeIframeAPIReady();
            }
        } else {
            Promise.all([
                loadScript(this, YouTubePath1),
                loadScript(this, YouTubePath2)
            ])
                .then(() => {
                    this.onYouTubeIframeAPIReady();
                })
                .catch(error => {
                    this.showErrorToast(error);
                });
        }
    }

    onPlayerError(e) {
        let explanation = '';
        if (e.data === 2) {
            explanation = 'Invalid YouTube ID';
        } else if (e.data === 5) {
            explanation =
                'The requested content cannot be played in an HTML5 player or another error related to the HTML5 player has occurred.';
        } else if (e.data === 100) {
            explanation =
                'The video requested was not found. This error occurs when a video has been removed (for any reason) or has been marked as private.';
        } else if (e.data === 101 || e.data === 150) {
            explanation =
                'The owner of the requested video does not allow it to be played in embedded players.';
        }

        this.showErrorToast(explanation);
    }

    showErrorToast(explanation) {
        const evt = new ShowToastEvent({
            title: 'Error loading YouTube player',
            message: explanation,
            variant: 'error'
        });
        this.dispatchEvent(evt);
    }

    onYouTubeIframeAPIReady() {
        const containerElem = this.template.querySelector('.wrapper');
        const playerElem = document.createElement('DIV');
        playerElem.className = 'player';
        containerElem.appendChild(playerElem);

        this.player = new window.YT.Player(playerElem, {
            height: '390',
            width: '100%',
            videoId: this.youTubeId,
            events: {
                onError: this.onPlayerError.bind(this)
            }
        });
    }
}

<template>
    <template if:true={youTubeId}>
        <div class="wrapper" lwc:dom="manual"></div>
    </template>
    <template if:false={youTubeId}>
        <lightning-card  title="Empty YouTube ID found. Please specify a YouTube ID."></lightning-card>
    </template>
</template>

Note: the two imported files need to be modified to support importing. Add the following lines to the end of each file, as noted here:

const YT1 = window.YT;
export { YT1 as YT };
6
  • That's the same playground you helped me with on this question: salesforce.stackexchange.com/questions/310786/… Jul 23 '20 at 20:31
  • I thought that what we did for the YouTube resources in the playground (putting them in a folder in the component and referencing them that way) was something that only applied to the playground. This technique is more fool proof than loading via static resources? Jul 23 '20 at 20:33
  • @MichaelSobczak Either way should work reliably. I found that some modifications are apparently necessary for the method in this answer, but in general, you can indeed load scripts via import from the same or other components. However, loadScript should also work, I was just trying to provide an alternative. I'm going to try to get this working and I'll update the answer when I can.
    – sfdcfox
    Jul 23 '20 at 21:38
  • I took the code from the playground and created a new component with it, copy/pasting the contents of each file exactly. I then added the component to a Lighting home page. I get the following error: Cannot read property 'parentNode' of undefined. Very strange. Jul 23 '20 at 22:14
  • @MichaelSobczak Ditto. I think it's a locker service issue. I'm trying to come up with an alternative.
    – sfdcfox
    Jul 23 '20 at 22:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.