I am trying to load a 3rd party script from web, instead of making a local copy of it and be able to use the 3rd party script's global variables and functions after the script loads.
Update:
Issue: Component below is not loading the script from 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!!'
}
}
I have modified Rahul Kumar's 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
}
You can dynamically load JS scripts and libraries on demand in your Angular 2/4 project using this technique.
Create ScriptStore
in script.store.ts that 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'}
];
Create script.service.ts to provide ScriptService
as an injectable service that will handle the loading of script files. Include this code:
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 ScriptService
wherever you need it and load scripts like this:
constructor(
private scriptService: ScriptService
) { }
ngOnInit() {
this.scriptService.load('filepicker', 'rangeSlider').then(data => {
console.log('script loaded ', data);
}).catch(error => console.log(error));
}
There are two ways that this can be accomplished.
.d.ts
and are basically an interface for the functions of the script. If there is not a pre-defined type definition file you can create one your self with the functions that you need. (I prefer this method because some IDEs will give you the method signature as part of the intellisense)any
;Example using AutoMapperTS:
Type Definition:
/// <reference path="../node_modules/automapper-ts/dist/automapper.d.ts" />
@Component({
selector: "my-app",
})
export class AppComponent {
constructor() {
automapper.map("JSON", "myType", jsonObj);
}
}
(The reference in this example may be different depending on your editor. This example is using Visual Studio. Try dragging the file you want to reference onto the editor window to see if your IDE will create the reference for you.)
Declaration:
declare var automapper: any;
@Component({
selector: "my-app",
})
export class AppComponent {
constructor() {
automapper.map("JSON", "myType", jsonObj);
}
}
The 3rd party JS file can be loaded using the standard <script>
tag import. The above methods are for the TS compiler so that it won't fail with an unknown variable exception.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With