[javascript] Angular에서 외부 스크립트를 동적으로로드하는 방법은 무엇입니까?

<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 {...}

나는 알 importES6에 의한 사양은 정적 및 타이프 라이터가 아니라 런타임에보다 transpiling 동안 해결입니다.

어쨌든 file.js가 CDN 또는 로컬 폴더에서로드되도록 구성 할 수 있습니까? Angular 2에게 스크립트를 동적으로로드하도록 지시하는 방법은 무엇입니까?



답변

system.js를 사용 System.import()하는 경우 런타임에 사용할 수 있습니다 .

export class MyAppComponent {
  constructor(){
    System.import('path/to/your/module').then(refToLoadedModule => {
      refToLoadedModule.someFunction();
    }
  );
}

웹팩을 사용하는 경우 다음을 통해 강력한 코드 분할 지원을 최대한 활용할 수 있습니다 require.ensure.

export class MyAppComponent {
  constructor() {
     require.ensure(['path/to/your/module'], require => {
        let yourModule = require('path/to/your/module');
        yourModule.someFunction();
     });
  }
}


답변

다음 기술을 사용하여 Angular 프로젝트에서 요청시 JS 스크립트 및 라이브러리를 동적으로로드 할 수 있습니다.

script.store.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 libs를로드하십시오.

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


답변

이 작동 할 수 있습니다. 이 코드는 버튼을 클릭하면 html 파일에 <script>태그를 동적으로 추가합니다 head.

const url = 'http://iknow.com/this/does/not/work/either/file.js';

export class MyAppComponent {
    loadAPI: Promise<any>;

    public buttonClicked() {
        this.loadAPI = new Promise((resolve) => {
            console.log('resolving promise...');
            this.loadScript();
        });
    }

    public loadScript() {
        console.log('preparing to load...')
        let node = document.createElement('script');
        node.src = url;
        node.type = 'text/javascript';
        node.async = true;
        node.charset = 'utf-8';
        document.getElementsByTagName('head')[0].appendChild(node);
    }
}


답변

@rahul kumars 답변을 수정하여 대신 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
}


답변

또 다른 옵션은 그 문제에 대한 scriptjs패키지 를 활용 하는 것입니다.

모든 URL에서 주문형 스크립트 리소스를로드 할 수 있습니다.

패키지를 설치하십시오 :

npm i scriptjs

정의를위한 입력scriptjs :

npm install --save @types/scriptjs

그런 다음 가져 오기 $script.get()방법 :

import { get } from 'scriptjs';

마지막으로 Google Maps 라이브러리에서는 스크립트 리소스를로드합니다.

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

데모


답변

파일에 다음 과 같이 여러 스크립트를 동적으로 로드 수 있습니다 component.ts.

 loadScripts() {
    const dynamicScripts = [
     'https://platform.twitter.com/widgets.js',
     '../../../assets/js/dummyjs.min.js'
    ];
    for (let i = 0; i < dynamicScripts.length; i++) {
      const 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);
    }
  }

생성자 안에서이 메소드를 호출합니다.

constructor() {
    this.loadScripts();
}

참고 : 더 많은 스크립트를 동적으로로드하려면 dynamicScripts배열에 추가하십시오 .


답변

새로운 렌더러 API 로이 코드 스 니펫을 수행했습니다.

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

그리고 이렇게 불러

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

스택 블리츠