Blog, Development, Howto

Including additional JS files to your PCF Control

According to the best practices, it’s recommended to use TypeScript to build PCF Controls but in some exceptional situations the only way is to load files with JavaScript “on-demand”. Here are 3 examples I’m aware of and I highly encourage you to leave a comment under this post if you have another use-case scenario:

  • When JS Library doesn’t have ts-version – I understand that’s a rare situation but nevertheless, that happens;
  • When JS Library is available/generated based on the “Security Key” added to the URL – the good example here is the “Google” address typeahead library
  • When JS Library is used to extend/change the standard behavior of the control. I used that approach in a few controls of my ABControlsToolkit in controls where I wanted to give the customizer/developer more flexibility.

In order to make the code of the “dynamic” load reusable and not limited to one file, I created a helper function that can accept the list of JavaScript files and return Promise so the calling code will be able to wait until all required files are loaded. Here is the code of that helper:

export const LoadJSResources = (webResources: string[]): Promise<void> => {
    return new Promise<void>((resolve, reject) => {
        if (!webResources || !webResources.length) {
            resolve();
            return;
        }

        const loadAllJSResources = webResources.map(webResource => LoadJSResource(webResource));

        Promise.all(loadAllJSResources)
            .then(() => {
                resolve();
            })
            .catch(reject);
    });
}

const LoadJSResource = (webResource: string): Promise<void> => {
    return new Promise<void>((resolve, reject) => {
        let scriptNode = document.createElement("script");
        scriptNode.setAttribute("type", "text/javascript");
        scriptNode.setAttribute("src", webResource);
        scriptNode.onload = () => {
            resolve();
        };

        scriptNode.onerror = () => {
            reject(`WebResource '${webResource}' was not loaded`)
        };

        document.head.appendChild(scriptNode);
    });
}

If you want to use that code – create a new file in your PCF project (let’s say you called the file JSLoader.ts), copy-paste the provided code, and use it. Here is an example of the code that uses JSLoader:

const myGoogleApiKey = 'My Super Secret Key';
const allWebResources = [
	`https://maps.googleapis.com/maps/api/js?libraries=places&language=en&key=${myGoogleApiKey}`,
	"/WebResources/ab_/SomeCustomJS.js"
];

LoadJSResources(allWebResources).then(() => {
	console.log('All required JS Files were loaded');
	//Add your logic if all the webresources were loaded successfully here
}, e => {
	console.error(`Something went wrong during the load of webresources - ${e}`);
	//Add your logic if some (or all) webresources failed to load
});

As you may see it’s possible to load both absolute and relative webresources the similar way.

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.