如何将第三方脚本从Web动态加载到Angular2组件中

时间:2016-06-09 15:12:43

标签: angular external-script

我正在尝试从网上加载第三方脚本,而不是制作它的本地副本,并且能够在脚本加载后使用第三方脚本的全局变量和函数。

更新

  • 以下是我在普通JavaScript中尝试实现的示例,点击Visa Checkout按钮打开Visa Checkout对话框: Plunker JS link
  • 以下是我需要帮助的Angular2版本: Plunker Angular2 link

问题:下面的组件未从Web加载脚本



import {Component} from '@angular/core'

@Component({
  selector: 'custom',
  providers: [],
  template: `
    <div>
      <h2>{{name}}</h2>
      <img class="v-button" role="button" alt="Visa Checkout" src="https://sandbox.secure.checkout.visa.com/wallet-services-web/xo/button.png">
      <script src="https://sandbox-assets.secure.checkout.visa.com/checkout-widget/resources/js/integration/v1/sdk.js">
</script>
    </div>
  `
})
export class CustomComponent {
  constructor() {
    this.name = 'Custom Component works!!'
  }
}
&#13;
&#13;
&#13;

3 个答案:

答案 0 :(得分:11)

您可以使用此技术在Angular 2/4项目中按需动态加载JS脚本和库。

script.store.ts 中创建Identity,其中包含本地或远程服务器上的路径以及名称将用于动态加载脚本:

ScriptStore

创建 script.service.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'} ]; 作为可注入服务,该服务将处理脚本文件的加载。包括此代码:

ScriptService

在您需要的地方注入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

答案 1 :(得分:2)

有两种方法可以实现。

  1. 引用要添加的第三方脚本的类型定义文件。类型定义文件通常以.d.ts结尾,基本上是脚本功能的接口。如果没有预定义的类型定义文件,您可以使用所需的功能创建自己的文件。 (我更喜欢这种方法,因为有些IDE会给你方法签名作为intellisense的一部分)
  2. 在TypeScript类的顶部创建一个变量,该变量表示您使用的库类型为any;
  3. 使用AutoMapperTS的示例:

    类型定义:

    /// <reference path="../node_modules/automapper-ts/dist/automapper.d.ts" />
    
    @Component({
        selector: "my-app",
    })
    export class AppComponent {
        constructor() {
            automapper.map("JSON", "myType", jsonObj);
        }
    }
    

    (此示例中的引用可能因编辑器而异。此示例使用的是Visual Studio。尝试将要引用的文件拖到编辑器窗口中,以查看IDE是否将为您创建引用。)< / p>

    <强>声明:

    declare var automapper: any;
    
    @Component({
        selector: "my-app",
    })
    export class AppComponent {
        constructor() {
            automapper.map("JSON", "myType", jsonObj);
        }
    }
    

    可以使用标准<script>标记导入加载第三方JS文件。上述方法适用于TS编译器,因此它不会因未知变量异常而失败。

答案 2 :(得分:2)

我修改了Rahul Kumar's answer,以便它使用Observables代替:

import { Injectable } from "@angular/core";
import { Observable } from "rxjs/Observable";
import { Observer } from "rxjs/Observer";

@Injectable()
export class ScriptLoaderService {
    private scripts: {ScriptModel}[] = [];

    public load(script: ScriptModel): Observable<ScriptModel> {
        return new Observable<ScriptModel>((observer: Observer<ScriptModel>) => {
            var existingScript = this.scripts.find(s => s.name == script.name);

            // Complete if already loaded
            if (existingScript && existingScript.loaded) {
                observer.next(existingScript);
                observer.complete();
            }
            else {
                // Add the script
                this.scripts = [...this.scripts, script];

                // Load the script
                let scriptElement = document.createElement("script");
                scriptElement.type = "text/javascript";
                scriptElement.src = script.src;

                scriptElement.onload = () => {
                    script.loaded = true;
                    observer.next(script);
                    observer.complete();
                };

                scriptElement.onerror = (error: any) => {
                    observer.error("Couldn't load script " + script.src);
                };

                document.getElementsByTagName('body')[0].appendChild(scriptElement);
            }
        });
    }
}

export interface ScriptModel {
    name: string,
    src: string,
    loaded: boolean
}