Динамически загружать внешний файл javascript из компонента Angular
Я создаю приложение Angular, используя Angular 4 и CLI. Я пытаюсь добавить виджет поиска SkyScanner в один из моих компонентов.
Пример виджета Skyscanner
Часть реализации требует добавления нового внешнего script:
<script src="https://widgets.skyscanner.net/widget-server/js/loader.js" async></script>
Я не уверен в правильности ссылки на этот файл. Если я добавлю script в мой файл index.html, виджет не загружается, если не выполняется полное обновление страницы. Я предполагаю, что script пытается манипулировать DOM при загрузке, а элементы не существуют, когда выполняется script.
Каков правильный способ загрузки script только при загрузке компонента, содержащего виджет Skyscanner?
Ответы
Ответ 1
Попробуйте загрузить внешний 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);
}
}
}
Ответ 2
У меня была та же проблема, но в моем случае я импортировал 10 библиотек в конце файла HTML, и у этих библиотек было много методов, слушателей, событий и т.д., И в моем случае мне не нужно было вызовите метод специально.
Пример о том, что у меня было:
<!-- 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() используйте метод для добавления библиотек, например:
""
<!-- app.component.ts -->
export class AppComponent implements OnInit {
title = 'app';
ngOnInit() {
this.loadScript('http://www.some-library.com/library.js');
this.loadScript('../assets/js/my-library.js');
}
}
public loadScript(url: string) {
const body = <HTMLDivElement> document.body;
const script = document.createElement('script');
script.innerHTML = '';
script.src = url;
script.async = false;
script.defer = true;
body.appendChild(script);
}
}
Это функционирует для меня. Я использую Angular 6, надеюсь, это поможет.
Ответ 3
Я сделал этот фрагмент кода
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');
}
РЕДАКТИРОВАТЬ: С новым рендерер 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;
}
StackBlitz
Ответ 4
добавьте loader.js
в папку с вашими активами, а затем в angular-cli.json
"scripts": ["./src/assets/loader.js",]
затем добавьте это в свой typings.d.ts
declare var skyscanner:any;
и вы сможете использовать его
skyscanner.load("snippets","2");
Ответ 5
Вы можете создать свою собственную директиву для загрузки 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>
Ответ 6
Вы можете сделать одну вещь
если у вас есть angular -cli.json
то вы можете объявить script
как
"scripts": ["../src/assets/js/loader.js"]
И затем объявите skyscanner в своем компоненте
like
declare var skyscanner:any;
Вот оно!
Надеюсь, что эта помощь вам поможет.
Ответ 7
Примечание: это специально для внешних ссылок js! Шаг 1. Рекомендуем добавить свой угловой скрипт в файл index.html внизу вашего тела! Я пробовал все другие способы, но не смог.
<!-- File Name: index.html and its inside src dir-->
<body class="">
<app-root></app-root>
<!-- Icons -->
<script src="https://unpkg.com/feather-icons/dist/feather.min.js"></script>
</body>
Ответ 8
Немного поздно, но я предпочитаю делать так (сервисный способ)....
import { Injectable } from '@angular/core';
import { Observable } from "rxjs";
interface Scripts {
name: string;
src: string;
}
export const ScriptStore: Scripts[] = [
{ name: 'script-a', src: 'assets/js/a.js' },
{ name: 'script-b', src: 'assets/js/b.js' },
{ name: 'script-c', src: 'assets/js/c.js' }
];
declare var document: any;
@Injectable()
export class FileInjectorService {
private scripts: any = {};
constructor() {
ScriptStore.forEach((script: any) => {
this.scripts[script.name] = {
loaded: false,
src: script.src
};
});
}
loadJS(...scripts: string[]) {
const promises: any[] = [];
scripts.forEach((script) => promises.push(this.loadJSFile(script)));
return Promise.all(promises);
}
loadJSFile(name: string) {
return new Promise((resolve, reject) => {
if (!this.scripts[name].loaded) {
let script = document.createElement('script');
script.type = 'text/javascript';
script.src = this.scripts[name].src;
if (script.readyState) {
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 {
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);
} else {
resolve({ script: name, loaded: true, status: 'Already Loaded' });
}
});
}
}
Тогда в моем компоненте я мог бы сделать что-то вроде:
ngOnInit() {
this.fileInjectorService.loadJS('script-a', 'script-c').then(data => {
// Loaded A and C....
}).catch(error => console.log(error));
}
Испытано в Angular 6/7
Ответ 9
Принятый ответ правильный, но не сработает, потому что браузеру требуется немного больше времени для анализа скрипта после его загрузки. Таким образом, если какая-либо переменная используется из загруженного скрипта, то ее необходимо использовать для события onload недавно созданного элемента html script. Я улучшил принятый ответ, как указано ниже -
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;
}