从Angular组件动态加载外部javascript文件

从Angular组件动态加载外部javascript文件,angular,typescript,skyscanner,Angular,Typescript,Skyscanner,我正在使用Angular 4和CLI创建Angular应用程序。我正在尝试将SkyScanner搜索小部件添加到我的一个组件中 部分实现需要添加新的外部脚本: <script src="https://widgets.skyscanner.net/widget-server/js/loader.js" async></script> 我不确定引用此文件的正确方式。如果我将脚本添加到index.html文件中,除非执行完整页面刷新,否则不会加载小部件。我假设脚本尝试

我正在使用Angular 4和CLI创建Angular应用程序。我正在尝试将SkyScanner搜索小部件添加到我的一个组件中

部分实现需要添加新的外部脚本:

<script src="https://widgets.skyscanner.net/widget-server/js/loader.js" async></script>

我不确定引用此文件的正确方式。如果我将脚本添加到index.html文件中,除非执行完整页面刷新,否则不会加载小部件。我假设脚本尝试在加载时操作DOM,并且在脚本运行时元素不存在


只有在加载包含Skyscanner小部件的组件时才加载脚本的正确方法是什么?

loader.js
添加到您的资产文件夹,然后添加到
angular cli.json

"scripts": ["./src/assets/loader.js",]
然后将其添加到您的
打字.d.ts

 declare var skyscanner:any;
你将能够使用它

  skyscanner.load("snippets","2");

尝试在组件加载时加载外部JavaScript,如下所示:

loadAPI: Promise<any>;

constructor() {        
    this.loadAPI = new Promise((resolve) => {
        this.loadScript();
        resolve(true);
    });
}

public loadScript() {        
    var isFound = false;
    var scripts = document.getElementsByTagName("script")
    for (var i = 0; i < scripts.length; ++i) {
        if (scripts[i].getAttribute('src') != null && scripts[i].getAttribute('src').includes("loader")) {
            isFound = true;
        }
    }

    if (!isFound) {
        var dynamicScripts = ["https://widgets.skyscanner.net/widget-server/js/loader.js"];

        for (var i = 0; i < dynamicScripts.length; i++) {
            let node = document.createElement('script');
            node.src = dynamicScripts [i];
            node.type = 'text/javascript';
            node.async = false;
            node.charset = 'utf-8';
            document.getElementsByTagName('head')[0].appendChild(node);
        }

    }
}
loadAPI:Promise;
构造函数(){
this.loadAPI=新承诺((解析)=>{
这个.loadScript();
决心(正确);
});
}
公共加载脚本(){
var isFound=false;
var scripts=document.getElementsByTagName(“脚本”)
对于(变量i=0;i
你可以做一件事

如果您有angular-cli.json

然后您可以向脚本声明

然后在组件中声明skyscanner

就这样


希望这对您有所帮助

我已经完成了这个代码片段

 addJsToElement(src: string): HTMLScriptElement {
    const script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = src;
    this.elementRef.nativeElement.appendChild(script);
    return script;
  }
然后像这样称呼它

this.addJsToElement('https://widgets.skyscanner.net/widget-server/js/loader.js').onload = () => {
        console.log('SkyScanner Tag loaded');
}
constructor(private renderer: Renderer2){}

 addJsToElement(src: string): HTMLScriptElement {
    const script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = src;
    this.renderer.appendChild(document.body, script);
    return script;
  }
编辑:使用新的渲染器Api,可以这样编写

this.addJsToElement('https://widgets.skyscanner.net/widget-server/js/loader.js').onload = () => {
        console.log('SkyScanner Tag loaded');
}
constructor(private renderer: Renderer2){}

 addJsToElement(src: string): HTMLScriptElement {
    const script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = src;
    this.renderer.appendChild(document.body, script);
    return script;
  }

您可以创建自己的指令来加载脚本,如下所示

import { Directive, OnInit, Input } from '@angular/core';

@Directive({
    selector: '[appLoadScript]'
})
export class LoadScriptDirective implements OnInit{

    @Input('script') param:  any;

    ngOnInit() {
        let node = document.createElement('script');
        node.src = this.param;
        node.type = 'text/javascript';
        node.async = false;
        node.charset = 'utf-8';
        document.getElementsByTagName('head')[0].appendChild(node);
    }

}
您可以在组件模板中的任何位置使用它,如下所示

<i appLoadScript  [script]="'script_file_path'"></i>

例如,要在组件中动态加载JQuery,请在组件的模板中插入以下代码

<i appLoadScript  [script]="'/assets/baker/js/jquery.min.js'"></i>

注意:这是专门针对外部js链接的! 第一步。建议将angular脚本添加到身体底部的index.html文件中!我尝试了所有其他方法,但都失败了


我也遇到了同样的问题,但在我的例子中,我在html文件末尾导入了10个库,这些库有很多方法、侦听器、事件等,在我的例子中,我不需要专门调用方法

关于我的例子:

<!-- app.component.html -->

<div> 
 ...
</div>

<script src="http://www.some-library.com/library.js">
<script src="../assets/js/my-library.js"> <!-- a route in my angular project -->

...
如前所述,它不起作用。然后,我找到了一些帮助我的东西:

  • 删除app.component.html中的脚本调用。您必须在app.component.ts文件中链接这些脚本

  • 在ngOnInit()中,使用方法附加库,例如:

  • ``

    
    导出类AppComponent实现OnInit{
    标题=‘应用程序’;
    恩戈尼尼特(){
    此.loadScript('http://www.some-library.com/library.js');
    这个.loadScript('../assets/js/my library.js');
    }
    }
    公共加载脚本(url:string){
    const body=document.body;
    const script=document.createElement('script');
    script.innerHTML='';
    script.src=url;
    script.async=false;
    script.deep=真;
    body.appendChild(脚本);
    }
    }
    

    它对我有用。我使用Angular 6,希望能有所帮助。

    有点晚了,但我更喜欢这种方式(服务方式)

    然后在我的组件中,我可以执行以下操作:

    ngOnInit() {
      this.fileInjectorService.loadJS('script-a', 'script-c').then(data => {
        // Loaded A and C....
      }).catch(error => console.log(error));
    }
    

    在Angular 6/7中测试,接受的答案是正确的,但不起作用,因为浏览器在下载脚本后解析脚本只需花费很少的时间。因此,如果从加载的脚本中使用任何变量,则需要在新创建的html脚本元素的onload事件中使用该变量。我已经改进了下面提到的公认答案-

    loadAPI: Promise<any>;
    
    constructor() {
        this.loadAPI = new Promise((resolve) => {
            let node = this.loadScript();
            if (node) {
                node.onload = () => {
                    resolve(true);
                };
            } else {
                resolve(true);
            }
        });
    }
    
    ngOnInit() {
        this.loadAPI
            .then((flag) => {
            //Do something when script is loaded and parsed by browser
        });
    }
    
    loadScript() {
        let node = undefined;
        let isFound = false;
        const scripts = document.getElementsByTagName('script')
        for (let i = 0; i < scripts.length; ++i) {
            // Check if script is already there in html
            if (scripts[i].getAttribute('src') != null && scripts[i].getAttribute('src').includes("loader")) {
              isFound = true;
            }
        }
    
        if (!isFound) {
            const dynamicScript = 'https://widgets.skyscanner.net/widget-server/js/loader.js';
            node = document.createElement('script');
            node.src = dynamicScript;
            node.type = 'text/javascript';
            node.async = false;
            node.charset = 'utf-8';
            document.getElementsByTagName('head')[0].appendChild(node);
            return node;
        }
        return node;
    }
    
    loadAPI:Promise;
    构造函数(){
    this.loadAPI=新承诺((解析)=>{
    让node=this.loadScript();
    如果(节点){
    node.onload=()=>{
    决心(正确);
    };
    }否则{
    决心(正确);
    }
    });
    }
    恩戈尼尼特(){
    这是loadAPI
    。然后((标志)=>{
    //在浏览器加载和解析脚本时执行某些操作
    });
    }
    loadScript(){
    让节点=未定义;
    让isFound=false;
    const scripts=document.getElementsByTagName('script')
    for(设i=0;i
    经过这么多的代码测试,这对我来说很有效

    ngOnInit() {
        this.loadFormAssets().then(() => {console.log("Script Loaded");}).catch(() => {console.log("Script Problem");});
      }
    
     public loadFormAssets() {
        return new Promise(resolve => {
    
          const scriptElement = document.createElement('script');
          scriptElement.src =this.urls.todojs;
          scriptElement.onload = resolve;
          document.body.appendChild(scriptElement);
    
          const scriptElement1 = document.createElement('script');
          scriptElement1.src =this.urls.vendorjs;
          scriptElement1.onload = resolve;
          document.body.appendChild(scriptElement1);
    
        });
      }
    

    在我的例子中,我必须加载一些相互依赖的不同文件(某种使用引导的东西,然后使用jquery插件,然后使用jquery),它们在加载时都立即初始化,假设它们在网页上同步加载。所有其他答案
    loadAPI: Promise<any>;
    
    constructor() {
        this.loadAPI = new Promise((resolve) => {
            let node = this.loadScript();
            if (node) {
                node.onload = () => {
                    resolve(true);
                };
            } else {
                resolve(true);
            }
        });
    }
    
    ngOnInit() {
        this.loadAPI
            .then((flag) => {
            //Do something when script is loaded and parsed by browser
        });
    }
    
    loadScript() {
        let node = undefined;
        let isFound = false;
        const scripts = document.getElementsByTagName('script')
        for (let i = 0; i < scripts.length; ++i) {
            // Check if script is already there in html
            if (scripts[i].getAttribute('src') != null && scripts[i].getAttribute('src').includes("loader")) {
              isFound = true;
            }
        }
    
        if (!isFound) {
            const dynamicScript = 'https://widgets.skyscanner.net/widget-server/js/loader.js';
            node = document.createElement('script');
            node.src = dynamicScript;
            node.type = 'text/javascript';
            node.async = false;
            node.charset = 'utf-8';
            document.getElementsByTagName('head')[0].appendChild(node);
            return node;
        }
        return node;
    }
    
    ngOnInit() {
        this.loadFormAssets().then(() => {console.log("Script Loaded");}).catch(() => {console.log("Script Problem");});
      }
    
     public loadFormAssets() {
        return new Promise(resolve => {
    
          const scriptElement = document.createElement('script');
          scriptElement.src =this.urls.todojs;
          scriptElement.onload = resolve;
          document.body.appendChild(scriptElement);
    
          const scriptElement1 = document.createElement('script');
          scriptElement1.src =this.urls.vendorjs;
          scriptElement1.onload = resolve;
          document.body.appendChild(scriptElement1);
    
        });
      }