How to load external scripts dynamically in Angular?

前端 未结 15 766
被撕碎了的回忆
被撕碎了的回忆 2020-11-22 06:48

I have this module which componentize the external library together with additional logic without adding the

相关标签:
15条回答
  • 2020-11-22 07:22

    @d123546 I faced the same issue and got it working now using ngAfterContentInit (Lifecycle Hook) in the component like this :

    import { Component, OnInit, AfterContentInit } from '@angular/core';
    import { Router } from '@angular/router';
    import { ScriptService } from '../../script.service';
    
    @Component({
        selector: 'app-players-list',
        templateUrl: './players-list.component.html',
        styleUrls: ['./players-list.component.css'],
        providers: [ ScriptService ]
    })
    export class PlayersListComponent implements OnInit, AfterContentInit {
    
    constructor(private router: Router, private script: ScriptService) {
    }
    
    ngOnInit() {
    }
    
    ngAfterContentInit() {
        this.script.load('filepicker', 'rangeSlider').then(data => {
        console.log('script loaded ', data);
        }).catch(error => console.log(error));
    }
    
    0 讨论(0)
  • 2020-11-22 07:22
    import { Injectable } from '@angular/core';
    import * as $ from 'jquery';
    
    interface Script {
        src: string;
        loaded: boolean;
    }
    
    @Injectable()
    export class ScriptLoaderService {
        public _scripts: Script[] = [];
    
        /**
         * @deprecated
         * @param tag
         * @param {string} scripts
         * @returns {Promise<any[]>}
         */
        load(tag, ...scripts: string[]) {
            scripts.forEach((src: string) => {
                if (!this._scripts[src]) {
                    this._scripts[src] = { src: src, loaded: false };
                }
            });
    
            const promises: any[] = [];
            scripts.forEach(src => promises.push(this.loadScript(tag, src)));
    
            return Promise.all(promises);
        }
    
        /**
         * Lazy load list of scripts
         * @param tag
         * @param scripts
         * @param loadOnce
         * @returns {Promise<any[]>}
         */
        loadScripts(tag, scripts, loadOnce?: boolean) {
            debugger;
            loadOnce = loadOnce || false;
    
            scripts.forEach((script: string) => {
                if (!this._scripts[script]) {
                    this._scripts[script] = { src: script, loaded: false };
                }
            });
    
            const promises: any[] = [];
            scripts.forEach(script => promises.push(this.loadScript(tag, script, loadOnce)));
    
            return Promise.all(promises);
        }
    
        /**
         * Lazy load a single script
         * @param tag
         * @param {string} src
         * @param loadOnce
         * @returns {Promise<any>}
         */
        loadScript(tag, src: string, loadOnce?: boolean) {
            debugger;
            loadOnce = loadOnce || false;
    
            if (!this._scripts[src]) {
                this._scripts[src] = { src: src, loaded: false };
            }
    
            return new Promise((resolve, _reject) => {
                // resolve if already loaded
                if (this._scripts[src].loaded && loadOnce) {
                    resolve({ src: src, loaded: true });
                } else {
                    // load script tag
                    const scriptTag = $('<script/>')
                        .attr('type', 'text/javascript')
                        .attr('src', this._scripts[src].src);
    
                    $(tag).append(scriptTag);
    
                    this._scripts[src] = { src: src, loaded: true };
                    resolve({ src: src, loaded: true });
                }
            });
        }
    
        reloadOnSessionChange() {
            window.addEventListener('storage', function(data) {
                if (data['key'] === 'token' && data['oldValue'] == null && data['newValue']) {
                    document.location.reload();
                }
            });
        }
    }
    
    0 讨论(0)
  • 2020-11-22 07:23

    I have a good way to dynamically load scripts! Now I use ng6, echarts4 (>700Kb ) ,ngx-echarts3 in my project. when I use them by ngx-echarts's docs, I need import echarts in angular.json : "scripts":["./node_modules/echarts/dist/echarts.min.js"] thus in the login module, page while loading scripts.js, this is big file! I don't want it.

    So, I think angular loads each module as a file, I can insert a router resolver to preload js, then begin the module loading!

    // PreloadScriptResolver.service.js

    /**动态加载js的服务 */
    @Injectable({
      providedIn: 'root'
    })
    export class PreloadScriptResolver implements Resolve<IPreloadScriptResult[]> {
      // Here import all dynamically js file
      private scripts: any = {
        echarts: { loaded: false, src: "assets/lib/echarts.min.js" }
      };
      constructor() { }
      load(...scripts: string[]) {
        const promises = scripts.map(script => this.loadScript(script));
        return Promise.all(promises);
      }
      loadScript(name: string): Promise<IPreloadScriptResult> {
        return new Promise((resolve, reject) => {
          if (this.scripts[name].loaded) {
            resolve({ script: name, loaded: true, status: 'Already Loaded' });
          } else {
            const script = document.createElement('script');
            script.type = 'text/javascript';
            script.src = this.scripts[name].src;
            script.onload = () => {
              this.scripts[name].loaded = true;
              resolve({ script: name, loaded: true, status: 'Loaded' });
            };
            script.onerror = (error: any) => reject({ script: name, loaded: false, status: 'Loaded Error:' + error.toString() });
            document.head.appendChild(script);
          }
        });
      }
    
      resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<IPreloadScriptResult[]> {
       return this.load(...route.routeConfig.data.preloadScripts);
      }
    }
    

    Then in the submodule-routing.module.ts ,import this PreloadScriptResolver:

    const routes: Routes = [
      {
        path: "",
        component: DashboardComponent,
        canActivate: [AuthGuardService],
        canActivateChild: [AuthGuardService],
        resolve: {
          preloadScripts: PreloadScriptResolver
        },
        data: {
          preloadScripts: ["echarts"]  // important!
        },
        children: [.....]
    }
    

    This code works well, and its promises that: After js file loaded, then module begin load! this Resolver can use in many routers

    0 讨论(0)
提交回复
热议问题