我有这个模块,它将外部库与额外的逻辑组件化,而不直接将<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 {...}
我注意到ES6规范的导入是静态的,并且是在TypeScript编译期间解析的,而不是在运行时。
总之,让它变得可配置,这样file。js就会从CDN或本地文件夹加载?
如何告诉Angular 2动态加载脚本?
Angular有防止用户直接干扰html输出的逻辑。所以你必须让Angular通过在Angular中给出那个方向来注入标签。json文件。
首先,您必须获取脚本文件。有两种方法:
下载脚本文件(例如。somelibrary.js)
把它放在资产文件夹中
把脚本的相对路径,放到angular的“scripts”部分。json文件:
"scripts": [
"src/assets/somelibrary.js"
]
使用npm/yarn安装脚本:
把脚本的相对路径,放到angular的“scripts”部分。json文件:
"scripts": [
"./node_modules/somelibrary/dist/somelibrary.min.js"
]
您可以像这样在组件中动态加载多个脚本。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数组中。
嗨,你可以使用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);
}
onload函数可以用来在脚本加载后调用脚本函数,如果你必须在ngOnInit()中进行调用,这是非常有用的。
这个解决方案对我很有效:
1)创建一个名为URLLoader的新类
export class URLLoader {
constructor() {
}
loadScripts() {
const dynamicScripts = [
'URL 1',
'URL 2',
'URL n'
];
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('app-root')[0].appendChild(node);
}
}
}
2)扩展类URLLoader并从组件类中调用loadScripts方法
export class AppComponent extends URLLoader implements OnInit {
constructor(){}
ngOnInit() {
super.loadScripts();
}
}
我希望能够:
Add a script when the app is being bootstrapped
Not do it from a component, because it doesn't feel like it's any component's responsibility
Not do it from a directive, because of the same reason as the component
Not do it from a service, because unless there's some kind of heavy logic related to an existing service, this doesn't belong IMO to a service
Avoid doing it in a module. A module could be fine but it's not as flexible as just using DI and since Angular 15 standalone components are stable so why bother with a module
也就是说,为了在应用程序引导之前做到这一点,这有点棘手。因为我们在那个阶段没有可用的渲染器,并且我们不能访问包含nativeElement的elementRef。
下面是我的看法:
export const YOUR_EXT_LIB_URL_TOKEN = new InjectionToken<string>('YOUR_EXT_LIB_URL_TOKEN');
export const YOUR_SETUP: Provider = {
provide: APP_INITIALIZER,
multi: true,
useFactory: (
doc: InjectionTokenType<typeof DOCUMENT>,
rendererFactory: RendererFactory2,
yourExternalLibToken: string,
) => {
const renderer = rendererFactory.createRenderer(null, null);
const script = renderer.createElement('script');
script.type = 'text/javascript';
script.src = yourExternalLibToken;
renderer.appendChild(doc.body, script);
return () => true;
},
deps: [DOCUMENT, RendererFactory2, YOUR_EXT_LIB_URL_TOKEN],
};
然后,您所要做的就是提供YOUR_EXT_LIB_URL_TOKEN并传递YOUR_SETUP提供程序。
这样,所有东西都是通过DI注入的,非常灵活。例如,您可以在共享库中提供YOUR_SETUP令牌,并在使用共享库的不同应用程序中提供YOUR_EXT_LIB_URL_TOKEN。