在 Angular 组件模板中添加脚本标签
Adding script tags in Angular component template
Angular2 自动从模板中删除 <script>
标签,以阻止人们将此功能用作 "poor's man" loader。
这里的问题是脚本标签目前有更多的用途,而不仅仅是加载代码或其他脚本文件。将来也有可能引入有关 <script>
标签的更多功能。
当前的一个用法是 JSON-LD,格式为
<script type="application/ld+json">
{
"@context":"http://schema.org",
"@type":"HealthClub",
...
}
</script>
通常建议的解决方法是通过 ngAfterViewInit
挂钩 dynamically add script tags 到文档,但这显然不是正确的 ng2 做法并且不会在服务器端工作,JSON -LD显然需要能够做到。
我们是否可以使用任何其他解决方法在 angular2 模板中包含 <script>
标签(即使标签在浏览器中是惰性的),或者这是框架过于自以为是的情况?如果在angular2中无法解决这种情况,可能还有哪些其他解决方案?
Angular2 没有向模板添加脚本标签的方法。
使用 require(...)
从组件 class 加载外部脚本是一种变通方法(我自己还没有尝试过)
动态添加脚本标签使用
constructor(private elementRef:ElementRef) {};
ngAfterViewInit() {
var s = document.createElement("script");
s.type = "text/javascript";
s.src = "http://somedomain.com/somescript";
this.elementRef.nativeElement.appendChild(s);
}
另见 angular2: including thirdparty js scripts in component
实际上
没有 Angular2 添加 script 标签 到模板的方法。
但你可以做一些技巧
首先 你将从 angular2 导入 AfterViewInit
和 ElementRef
像这样:
import {Component,AfterViewInit,ElementRef} from 'Angular2/core';
然后 你会像这样在你的 class 中实现它们:
export class example1 implements AfterViewInit{}
这是一个非常简单的 javascript dom 技巧你会做
export class example1 implements AfterViewInit{
ngAfterViewInit()
{
var s=document.createElement("script");
s.type="text/javascript";
s.innerHTML="console.log('done');"; //inline script
s.src="path/test.js"; //external script
}
}
这里的聚会可能有点晚了,但由于上述答案不适用于 Angular SSR(例如 document is not defined
服务器端或 document.createElement is not a function
),我决定编写适用于 Angular 4+, 服务器和浏览器上下文的版本:
组件实现
import { Renderer2, OnInit, Inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';
class MyComponent implements OnInit {
constructor(
private _renderer2: Renderer2,
@Inject(DOCUMENT) private _document: Document
) { }
public ngOnInit() {
let script = this._renderer2.createElement('script');
script.type = `application/ld+json`;
script.text = `
{
"@context": "https://schema.org"
/* your schema.org microdata goes here */
}
`;
this._renderer2.appendChild(this._document.body, script);
}
}
服务实施
注意:服务不能直接使用 Renderer2
。事实上,渲染一个元素 应该由一个组件 来完成。但是,您可能会发现自己处于想要在页面上自动创建 JSON-LD script
标记的情况。例如,一种情况可能是在路线导航更改事件上调用此类功能。因此我决定添加一个在 Service
上下文中工作的版本。
import { Renderer2, Inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';
/**
* Use a Service to automate creation of JSON-LD Microdata.
*/
class MyService {
constructor(
@Inject(DOCUMENT) private _document: Document
) { }
/**
* Set JSON-LD Microdata on the Document Body.
*
* @param renderer2 The Angular Renderer
* @param data The data for the JSON-LD script
* @returns Void
*/
public setJsonLd(renderer2: Renderer2, data: any): void {
let script = renderer2.createElement('script');
script.type = 'application/ld+json';
script.text = `${JSON.stringify(data)}`;
renderer2.appendChild(this._document.body, script);
}
}
以下适用于 Angular 5.2.7:
所需的导入是:
import { Inject, AfterViewInit, ElementRef } from '@angular/core';
import { DOCUMENT } from '@angular/common';
实施 AfterViewInit:
export class HeroesComponent implements AfterViewInit {
如果您的组件实现了多个接口,请用逗号分隔它们;例如:
export class HeroesComponent implements OnInit, AfterViewInit {
将以下参数传递给构造函数:
constructor(@Inject(DOCUMENT) private document, private elementRef: ElementRef) { }
添加视图的ngAfterViewInit方法life-cycle:
ngAfterViewInit() {
const s = this.document.createElement('script');
s.type = 'text/javascript';
s.src = '//external.script.com/script.js';
const __this = this; //to store the current instance to call
//afterScriptAdded function on onload event of
//script.
s.onload = function () { __this.afterScriptAdded(); };
this.elementRef.nativeElement.appendChild(s);
}
添加afterScriptAdded成员函数。
该函数将在外部脚本加载成功后调用。所以你要从外部js使用的属性或函数,都会在这个函数体中访问。
afterScriptAdded() {
const params= {
width: '350px',
height: '420px',
};
if (typeof (window['functionFromExternalScript']) === 'function') {
window['functionFromExternalScript'](params);
}
}
const head = document.getElementsByTagName('head')[0];
const _js = document.createElement('script');
_js.type = 'text/javascript';
_js.appendChild(document.createTextNode('{your js code here}'));
head.appendChild(_js);
这可以放在任何地方,这是一种很好的方法
我添加了 js 脚本动态加载组件 init life hook 以下面的方式:
private loadChatWithScript() {
let chatScript = document.createElement("script");
chatScript.type = "text/javascript";
chatScript.async = true;
chatScript.src = "https://chat-assets.frontapp.com/v1/chat.bundle.js";
document.body.appendChild(chatScript);
let chatScriptConfiguration = document.createElement("script");
chatScriptConfiguration.type = "text/javascript";
chatScriptConfiguration.async = true;
chatScriptConfiguration.innerHTML = "window.FCSP = 'some-key-123'";
document.body.appendChild(chatScriptConfiguration);
}
Gentleman/Gentlewoman,请看
添加 Pixel Scripts 这可能会节省您的时间,此解决方案比任何阐述都更容易:
- 仅适用于首次加载的页面 -
在你的Angular项目中找到index.html
,将它调整成这样(将你的脚本放在脚本标签中):
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My Angular Application</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
<script>
// Put your script here
alert('Test me, Angular hero!');
</script>
</body>
</html>
对于在 angular 组件模板中使用脚本,我建议使用@Nicky 的答案。
Angular2 自动从模板中删除 <script>
标签,以阻止人们将此功能用作 "poor's man" loader。
这里的问题是脚本标签目前有更多的用途,而不仅仅是加载代码或其他脚本文件。将来也有可能引入有关 <script>
标签的更多功能。
当前的一个用法是 JSON-LD,格式为
<script type="application/ld+json">
{
"@context":"http://schema.org",
"@type":"HealthClub",
...
}
</script>
通常建议的解决方法是通过 ngAfterViewInit
挂钩 dynamically add script tags 到文档,但这显然不是正确的 ng2 做法并且不会在服务器端工作,JSON -LD显然需要能够做到。
我们是否可以使用任何其他解决方法在 angular2 模板中包含 <script>
标签(即使标签在浏览器中是惰性的),或者这是框架过于自以为是的情况?如果在angular2中无法解决这种情况,可能还有哪些其他解决方案?
Angular2 没有向模板添加脚本标签的方法。
使用 require(...)
从组件 class 加载外部脚本是一种变通方法(我自己还没有尝试过)
动态添加脚本标签使用
constructor(private elementRef:ElementRef) {};
ngAfterViewInit() {
var s = document.createElement("script");
s.type = "text/javascript";
s.src = "http://somedomain.com/somescript";
this.elementRef.nativeElement.appendChild(s);
}
另见 angular2: including thirdparty js scripts in component
实际上
没有 Angular2 添加 script 标签 到模板的方法。
但你可以做一些技巧
首先 你将从 angular2 导入 AfterViewInit
和 ElementRef
像这样:
import {Component,AfterViewInit,ElementRef} from 'Angular2/core';
然后 你会像这样在你的 class 中实现它们:
export class example1 implements AfterViewInit{}
这是一个非常简单的 javascript dom 技巧你会做
export class example1 implements AfterViewInit{
ngAfterViewInit()
{
var s=document.createElement("script");
s.type="text/javascript";
s.innerHTML="console.log('done');"; //inline script
s.src="path/test.js"; //external script
}
}
这里的聚会可能有点晚了,但由于上述答案不适用于 Angular SSR(例如 document is not defined
服务器端或 document.createElement is not a function
),我决定编写适用于 Angular 4+, 服务器和浏览器上下文的版本:
组件实现
import { Renderer2, OnInit, Inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';
class MyComponent implements OnInit {
constructor(
private _renderer2: Renderer2,
@Inject(DOCUMENT) private _document: Document
) { }
public ngOnInit() {
let script = this._renderer2.createElement('script');
script.type = `application/ld+json`;
script.text = `
{
"@context": "https://schema.org"
/* your schema.org microdata goes here */
}
`;
this._renderer2.appendChild(this._document.body, script);
}
}
服务实施
注意:服务不能直接使用 Renderer2
。事实上,渲染一个元素 应该由一个组件 来完成。但是,您可能会发现自己处于想要在页面上自动创建 JSON-LD script
标记的情况。例如,一种情况可能是在路线导航更改事件上调用此类功能。因此我决定添加一个在 Service
上下文中工作的版本。
import { Renderer2, Inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';
/**
* Use a Service to automate creation of JSON-LD Microdata.
*/
class MyService {
constructor(
@Inject(DOCUMENT) private _document: Document
) { }
/**
* Set JSON-LD Microdata on the Document Body.
*
* @param renderer2 The Angular Renderer
* @param data The data for the JSON-LD script
* @returns Void
*/
public setJsonLd(renderer2: Renderer2, data: any): void {
let script = renderer2.createElement('script');
script.type = 'application/ld+json';
script.text = `${JSON.stringify(data)}`;
renderer2.appendChild(this._document.body, script);
}
}
以下适用于 Angular 5.2.7:
所需的导入是:
import { Inject, AfterViewInit, ElementRef } from '@angular/core';
import { DOCUMENT } from '@angular/common';
实施 AfterViewInit:
export class HeroesComponent implements AfterViewInit {
如果您的组件实现了多个接口,请用逗号分隔它们;例如:
export class HeroesComponent implements OnInit, AfterViewInit {
将以下参数传递给构造函数:
constructor(@Inject(DOCUMENT) private document, private elementRef: ElementRef) { }
添加视图的ngAfterViewInit方法life-cycle:
ngAfterViewInit() {
const s = this.document.createElement('script');
s.type = 'text/javascript';
s.src = '//external.script.com/script.js';
const __this = this; //to store the current instance to call
//afterScriptAdded function on onload event of
//script.
s.onload = function () { __this.afterScriptAdded(); };
this.elementRef.nativeElement.appendChild(s);
}
添加afterScriptAdded成员函数。
该函数将在外部脚本加载成功后调用。所以你要从外部js使用的属性或函数,都会在这个函数体中访问。
afterScriptAdded() {
const params= {
width: '350px',
height: '420px',
};
if (typeof (window['functionFromExternalScript']) === 'function') {
window['functionFromExternalScript'](params);
}
}
const head = document.getElementsByTagName('head')[0];
const _js = document.createElement('script');
_js.type = 'text/javascript';
_js.appendChild(document.createTextNode('{your js code here}'));
head.appendChild(_js);
这可以放在任何地方,这是一种很好的方法
我添加了 js 脚本动态加载组件 init life hook 以下面的方式:
private loadChatWithScript() {
let chatScript = document.createElement("script");
chatScript.type = "text/javascript";
chatScript.async = true;
chatScript.src = "https://chat-assets.frontapp.com/v1/chat.bundle.js";
document.body.appendChild(chatScript);
let chatScriptConfiguration = document.createElement("script");
chatScriptConfiguration.type = "text/javascript";
chatScriptConfiguration.async = true;
chatScriptConfiguration.innerHTML = "window.FCSP = 'some-key-123'";
document.body.appendChild(chatScriptConfiguration);
}
Gentleman/Gentlewoman,请看
添加 Pixel Scripts 这可能会节省您的时间,此解决方案比任何阐述都更容易:
- 仅适用于首次加载的页面 -
在你的Angular项目中找到index.html
,将它调整成这样(将你的脚本放在脚本标签中):
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My Angular Application</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
<script>
// Put your script here
alert('Test me, Angular hero!');
</script>
</body>
</html>
对于在 angular 组件模板中使用脚本,我建议使用@Nicky 的答案。