在 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 导入 AfterViewInitElementRef 像这样:

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 的答案。