angularangular-cliexternal-js

Using external javascript libraries in angular 2 lazy loaded module, not index.html


So I want to include an external JS library, a carousel slider in the is case, to my Angular 2 app. I have seen a lot of tutorials that show how it can be added I have done that successfully, but referencing the library on index.html.

This as you will know, will load the library every time the app is visited, regardless of whether they visit the components needing the carousel. Since it's pretty hefty, I only want to load it where it is needed, which is inside a lazy loaded module.

I haven't tried, but I'm sure I could just chuck the script tag in the component that uses it, but this doesn't feel right to me.

There must be a right way. What is it!?

Thanks!


Solution

  • We are doing this in one of our projects to load js libraries dynamically

    ScriptStore.ts that will contain the path of the script either locally or on a remote server and a name that will be used to load the script dynamically

     interface Scripts {
        name: string;
        src: string;
    }  
    export const ScriptStore: Scripts[] = [
        {name: 'filepicker', src: 'https://api.filestackapi.com/filestack.js'},
        { name: 'rangeSlider', src: '../../../assets/js/ion.rangeSlider.min.js' }
    ];
    

    script.service.ts is an injectable service that will handle the loading of script, copy script.service.ts as it is

    import {Injectable} from "@angular/core";
    import {ScriptStore} from "./script.store";
    
    declare var document: any;
    
    @Injectable()
    export class Script {
    
    private scripts: any = {};
    
    constructor() {
        ScriptStore.forEach((script: any) => {
            this.scripts[script.name] = {
                loaded: false,
                src: script.src
            };
        });
    }
    
    load(...scripts: string[]) {
        var promises: any[] = [];
        scripts.forEach((script) => promises.push(this.loadScript(script)));
        return Promise.all(promises);
    }
    
    loadScript(name: string) {
        return new Promise((resolve, reject) => {
            //resolve if already loaded
            if (this.scripts[name].loaded) {
                resolve({script: name, loaded: true, status: 'Already Loaded'});
            }
            else {
                //load script
                let script = document.createElement('script');
                script.type = 'text/javascript';
                script.src = this.scripts[name].src;
                if (script.readyState) {  //IE
                    script.onreadystatechange = () => {
                        if (script.readyState === "loaded" || script.readyState === "complete") {
                            script.onreadystatechange = null;
                            this.scripts[name].loaded = true;
                            resolve({script: name, loaded: true, status: 'Loaded'});
                        }
                    };
                } else {  //Others
                    script.onload = () => {
                        this.scripts[name].loaded = true;
                        resolve({script: name, loaded: true, status: 'Loaded'});
                    };
                }
                script.onerror = (error: any) => resolve({script: name, loaded: false, status: 'Loaded'});
                document.getElementsByTagName('head')[0].appendChild(script);
            }
        });
    }
    
    }
    

    Inject this ScriptService wherever you need it and load js libs like this

    this.script.load('filepicker', 'rangeSlider').then(data => {
                console.log('script loaded ', data);
            }).catch(error => console.log(error));