Cal*_*aNN 116 javascript typescript ecmascript-6 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 {...}
Run Code Online (Sandbox Code Playgroud)
我注意到import
ES6规范是静态的,并且在TypeScript转换过程中而不是在运行时解析.
无论如何要使它可配置,以便file.js将从CDN或本地文件夹加载?如何告诉Angular 2动态加载脚本?
Rah*_*mar 120
您可以使用以下技术在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'}
];
Run Code Online (Sandbox Code Playgroud)
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);
}
});
}
}
Run Code Online (Sandbox Code Playgroud)
ScriptService
在您需要的地方注入它并加载像这样的js库
this.script.load('filepicker', 'rangeSlider').then(data => {
console.log('script loaded ', data);
}).catch(error => console.log(error));
Run Code Online (Sandbox Code Playgroud)
dre*_*ore 52
如果您正在使用system.js,则可以System.import()
在运行时使用:
export class MyAppComponent {
constructor(){
System.import('path/to/your/module').then(refToLoadedModule => {
refToLoadedModule.someFunction();
}
);
}
Run Code Online (Sandbox Code Playgroud)
如果您正在使用webpack,您可以充分利用其强大的代码拆分支持require.ensure
:
export class MyAppComponent {
constructor() {
require.ensure(['path/to/your/module'], require => {
let yourModule = require('path/to/your/module');
yourModule.someFunction();
});
}
}
Run Code Online (Sandbox Code Playgroud)
ng-*_*ren 40
这可能会奏效.此代码在单击按钮时动态地将<script>
标记附加到head
html文件的标记.
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);
}
}
Run Code Online (Sandbox Code Playgroud)
Joe*_*oel 21
我修改了@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
}
Run Code Online (Sandbox Code Playgroud)
Vad*_*hev 13
另一种选择是利用scriptjs
包装来解决这个问题
允许您从任何URL按需加载脚本资源
例
安装包:
npm i scriptjs
Run Code Online (Sandbox Code Playgroud)
npm install --save @types/scriptjs
Run Code Online (Sandbox Code Playgroud)
然后导入$script.get()
方法:
import { get } from 'scriptjs';
Run Code Online (Sandbox Code Playgroud)
最后加载脚本资源,在我们的案例中谷歌地图库:
export class AppComponent implements OnInit {
ngOnInit() {
get("https://maps.googleapis.com/maps/api/js?key=", () => {
//Google Maps library has been loaded...
});
}
}
Run Code Online (Sandbox Code Playgroud)
Asw*_*kan 12
您可以在文件中动态加载多个脚本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);
}
}
Run Code Online (Sandbox Code Playgroud)
并在构造函数中调用此方法,
constructor() {
this.loadScripts();
}
Run Code Online (Sandbox Code Playgroud)
注意:要动态加载更多脚本,请将它们添加到dynamicScripts
数组中.
Het*_*dev 11
嗨,您只需几行代码即可使用 Renderer2 和 elementRef:
constructor(private readonly elementRef: ElementRef,
private renderer: Renderer2) {
}
ngOnInit() {
const script = this.renderer.createElement('script');
script.src = 'http://iknow.com/this/does/not/work/either/file.js';
script.onload = () => {
console.log('script loaded');
initFile();
};
this.renderer.appendChild(this.elementRef.nativeElement, script);
}
Run Code Online (Sandbox Code Playgroud)
该onload
函数可用于在脚本加载后调用脚本函数,如果您必须在 ngOnInit() 中进行调用,这将非常有用
我已经用新的渲染器 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;
}
Run Code Online (Sandbox Code Playgroud)
然后这样称呼
this.addJsToElement('https://widgets.skyscanner.net/widget-server/js/loader.js').onload = () => {
console.log('SkyScanner Tag loaded');
}
Run Code Online (Sandbox Code Playgroud)
小智 6
我有一个动态加载脚本的好方法!现在我在我的项目中使用 ng6, echarts4 (>700Kb ) ,ngx-echarts3。当我通过 ngx-echarts 的文档使用它们时,我需要在 angular.json 中导入 echarts : "scripts":["./node_modules/echarts/dist/echarts.min.js"] 因此在登录模块中,加载脚本时的页面.js,这是一个大文件!我不要。
所以,我认为angular将每个模块作为一个文件加载,我可以插入一个路由器解析器来预加载js,然后开始加载模块!
// PreloadScriptResolver.service.js
/**????js??? */
@Injectable({
providedIn: 'root'
})
export class PreloadScriptResolver implements Resolve<IPreloadScriptResult[]> {
// Here import all dynamically js file
private scripts: any = {
echarts: { loaded: false, src: "assets/lib/echarts.min.js" }
};
constructor() { }
load(...scripts: string[]) {
const promises = scripts.map(script => this.loadScript(script));
return Promise.all(promises);
}
loadScript(name: string): Promise<IPreloadScriptResult> {
return new Promise((resolve, reject) => {
if (this.scripts[name].loaded) {
resolve({ script: name, loaded: true, status: 'Already Loaded' });
} else {
const script = document.createElement('script');
script.type = 'text/javascript';
script.src = this.scripts[name].src;
script.onload = () => {
this.scripts[name].loaded = true;
resolve({ script: name, loaded: true, status: 'Loaded' });
};
script.onerror = (error: any) => reject({ script: name, loaded: false, status: 'Loaded Error:' + error.toString() });
document.head.appendChild(script);
}
});
}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<IPreloadScriptResult[]> {
return this.load(...route.routeConfig.data.preloadScripts);
}
}
Run Code Online (Sandbox Code Playgroud)
然后在 submodule-routing.module.ts 中导入这个 PreloadScriptResolver:
const routes: Routes = [
{
path: "",
component: DashboardComponent,
canActivate: [AuthGuardService],
canActivateChild: [AuthGuardService],
resolve: {
preloadScripts: PreloadScriptResolver
},
data: {
preloadScripts: ["echarts"] // important!
},
children: [.....]
}
Run Code Online (Sandbox Code Playgroud)
这段代码运行良好,它的承诺是:加载 js 文件后,然后模块开始加载!这个解析器可以在许多路由器中使用
Angular 通用解决方案;在加载脚本以播放视频之前,我需要等待特定元素出现在页面上。
import {Inject, Injectable, PLATFORM_ID} from '@angular/core';
import {isPlatformBrowser} from "@angular/common";
@Injectable({
providedIn: 'root'
})
export class ScriptLoaderService {
constructor(
@Inject(PLATFORM_ID) private platformId: Object,
) {
}
load(scriptUrl: string) {
if (isPlatformBrowser(this.platformId)) {
let node: any = document.createElement('script');
node.src = scriptUrl;
node.type = 'text/javascript';
node.async = true;
node.charset = 'utf-8';
document.getElementsByTagName('head')[0].appendChild(node);
}
}
}
Run Code Online (Sandbox Code Playgroud)
对于那些也想动态加载样式的人。 (基于@Rahul Kumar 的精彩回答)
脚本.store.ts
interface Scripts {
name: string;
src: string;
}
export const StyleStore: Scripts[] = [
{ name: 'fancybox-css', src: 'https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.css' }
];
export const ScriptStore: Scripts[] = [
{ name: 'jquery', src: 'https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js' },
{ name: 'other', src: '[other script source]'}
];
Run Code Online (Sandbox Code Playgroud)
脚本加载器.service.ts
import { Injectable } from '@angular/core';
import { ScriptStore, StyleStore } from '../../stores/script.store';
@Injectable({
providedIn: 'root'
})
export class ScriptLoaderService {
private scripts: any = {};
private styles: any = {};
constructor() {
ScriptStore.forEach((script: any) => {
this.scripts[script.name] = {
loaded: false,
src: script.src
};
});
StyleStore.forEach((script: any) => {
this.styles[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);
}
loadStyles(...scripts: string[]) {
var promises: any[] = [];
scripts.forEach((script) => promises.push(this.loadStyle(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') as any;
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);
}
});
}
loadStyle(name: string) {
return new Promise((resolve, reject) => {
//resolve if already loaded
if (this.styles[name].loaded) {
resolve({ script: name, loaded: true, status: 'Already Loaded' });
}
else {
//load style
let style = document.createElement('link') as any;
style.type = "text/css";
style.rel = "stylesheet";
style.href = this.styles[name].src;
if (style.readyState) { //IE
style.onreadystatechange = () => {
if (style.readyState === "loaded" || style.readyState === "complete") {
style.onreadystatechange = null;
this.styles[name].loaded = true;
resolve({ style: name, loaded: true, status: 'Loaded' });
}
};
} else { //Others
style.onload = () => {
this.styles[name].loaded = true;
resolve({ style: name, loaded: true, status: 'Loaded' });
};
}
style.onerror = (error: any) => resolve({ style: name, loaded: false, status: 'Loaded' });
document.getElementsByTagName('head')[0].appendChild(style);
}
});
}
}
Run Code Online (Sandbox Code Playgroud)
应用程序组件.ts
constructor(private scriptLoaderService: ScriptLoaderService) {
this.scriptLoaderService.loadStyles('fancybox-css').then(x => {
this.scriptLoaderService.load('jquery', 'fancybox').then(data => {
}).catch(error => console.log(error));
});
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
140636 次 |
最近记录: |