javascript - How to load external scripts dynamically in Angular?

ID : 20094

viewed : 25

Tags : javascriptangulartypescriptecmascript-6javascript

Top 5 Answer for javascript - How to load external scripts dynamically in Angular?

vote vote

98

You can use following technique to dynamically load JS scripts and libraries on demand in your Angular project.

script.store.ts will contain the path of the script either locally or on a remote server and a name that will be used to load the script dynamically

 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 is an injectable service that will handle the loading of script, copy script.service.ts as it is

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

Inject this ScriptService wherever you need it and load js libs like this

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

90

If you're using system.js, you can use System.import() at runtime:

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

If you're using webpack, you can take full advantage of its robust code splitting support with require.ensure :

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

79

This might work. This Code dynamically appends the <script> tag to the head of the html file on button clicked.

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

70

I have modified @rahul kumars answer, so that it uses Observables instead:

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

58

Yet another option would be to utilize scriptjs package for that matter which

allows you to load script resources on-demand from any URL

Example

Install the package:

npm i scriptjs 

and type definitions for scriptjs:

npm install --save @types/scriptjs 

Then import $script.get() method:

import { get } from 'scriptjs'; 

and finally load script resource, in our case Google Maps library:

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

Demo

Top 3 video Explaining javascript - How to load external scripts dynamically in Angular?

Related QUESTION?