我有这个模块,它将外部库与额外的逻辑组件化,而不直接将<script>标记添加到index.html中:

import 'http://external.com/path/file.js'
//import '../js/file.js'

@Component({
    selector: 'my-app',
    template: `
        <script src="http://iknow.com/this/does/not/work/either/file.js"></script>
        <div>Template</div>`
})
export class MyAppComponent {...}

我注意到ES6规范的导入是静态的,并且是在TypeScript编译期间解析的,而不是在运行时。

总之,让它变得可配置,这样file。js就会从CDN或本地文件夹加载? 如何告诉Angular 2动态加载脚本?


当前回答

我有一个很好的方法来动态加载脚本! 现在我在我的项目中使用ng6, echarts4 (>700Kb),ngx-echarts3。当我通过ngx-echarts的文档使用它们时,我需要在angular中导入echarts。json: “脚本”:["。/ node_modules / echarts / dist / echarts.min.js”) 因此在登录模块中,页面同时加载scripts.js,这是一个大文件!我不想要。

所以,我认为angular将每个模块作为一个文件加载,我可以插入一个路由器解析器来预加载js,然后开始加载模块!

/ / 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);
  }
}

然后在子模块-routing.module中。导入PreloadScriptResolver:

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

这段代码工作得很好,它承诺:js文件加载后,然后模块开始加载!这个解析器可以在许多路由器中使用

其他回答

还有一种选择是利用scriptjs包来解决这个问题

允许您按需从任何URL加载脚本资源

例子

安装包:

npm i scriptjs

以及scriptjs的类型定义:

npm install --save @types/scriptjs

然后导入$script.get()方法:

import { get } from 'scriptjs';

最后加载脚本资源,在我们的例子中是谷歌Maps库:

export class AppComponent implements OnInit {
  ngOnInit() {
    get("https://maps.googleapis.com/maps/api/js?key=", () => {
        //Google Maps library has been loaded...
    });
  }
}

Demo

@d123546 我遇到了同样的问题,现在使用ngAfterContentInit(生命周期钩子)在组件中工作,就像这样:

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));
}

在我的例子中,我使用上述技术加载了js和css visjs文件-这非常有效。我从ngOnInit()中调用新函数

注意:我不能通过简单地添加一个<script>和<link>标签到html模板文件来加载它。

loadVisJsScript() { console.log('Loading visjs js/css files...'); let script = document.createElement('script'); script.src = "../../assets/vis/vis.min.js"; script.type = 'text/javascript'; script.async = true; script.charset = 'utf-8'; document.getElementsByTagName('head')[0].appendChild(script); let link = document.createElement("link"); link.type = "stylesheet"; link.href = "../../assets/vis/vis.min.css"; document.getElementsByTagName('head')[0].appendChild(link); }

Angular的通用解决方案;在加载播放视频的脚本之前,我需要等待页面上的特定元素。

import {Inject, Injectable, PLATFORM_ID} from '@angular/core';
import {isPlatformBrowser} from "@angular/common";

@Injectable({
  providedIn: 'root'
})
export class ScriptLoaderService {

  constructor(
    @Inject(PLATFORM_ID) private platformId: Object,
  ) {
  }

  load(scriptUrl: string) {
    if (isPlatformBrowser(this.platformId)) {
      let node: any = document.createElement('script');
      node.src = scriptUrl;
      node.type = 'text/javascript';
      node.async = true;
      node.charset = 'utf-8';
      document.getElementsByTagName('head')[0].appendChild(node);
    }
  }
}

你可以使用以下技巧 在你的Angular项目中根据需要动态加载JS脚本和库。

ts将包含脚本在本地或远程服务器上的路径,以及用于动态加载脚本的名称

 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是一个可注入的服务,它将处理脚本的加载,原样复制script.service.ts

import {Injectable} from "@angular/core";
import {ScriptStore} from "./script.store";

declare var document: any;

@Injectable()
export class ScriptService {

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);
        }
    });
}

}

将这个ScriptService注入到任何你需要它的地方,并像这样加载js库

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