62

Angular2 会<script>自动从模板中删除标签,以阻止人们将此功能用作“穷人”加载器

这里的问题是脚本标签目前的用途不仅仅是加载代码或其他脚本文件。<script>将来也有可能引入围绕标签的更多功能。

目前的一种用途是 JSON-LD,它采用以下格式

<script type="application/ld+json">
{
    "@context":"http://schema.org",
    "@type":"HealthClub",
    ...
}
</script>

通常建议的解决方法是通过钩子动态地将脚本标签添加到文档中ngAfterViewInit,但这显然不是正确的 ng2 做法,并且不会在服务器端工作,而 JSON-LD 显然需要能够做到这一点。

是否有任何其他解决方法可用于<script>在 angular2 模板中包含标签(即使标签在浏览器中是惰性的),或者这是框架过于固执己见的情况?如果在 angular2 中无法解决这种情况,可能存在哪些其他解决方案?

4

7 回答 7

74

在这里聚会可能有点晚了,但由于上述答案不适用于 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直接使用。事实上,渲染一个元素应该是由一个 Component 来完成的。但是,您可能会发现自己想要在script页面上自动创建 JSON-LD 标记。例如,一种情况可能是在路线导航更改事件上调用此类函数。因此,我决定添加一个在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);
    }
}
于 2017-04-22T13:13:10.463 回答
24

没有 Angular2 方法可以将脚本标签添加到模板中。

提到了使用require(...)从组件类加载外部脚本作为一种解决方法(我自己没有尝试过)

要动态添加脚本标签,请使用

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:在组件中包含第三方 js 脚本

于 2016-06-29T04:01:28.023 回答
19

以下适用于 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 方法:

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);
    }
  }
于 2018-03-06T05:59:33.400 回答
9

实际上 没有Angular2方法可以将脚本标签添加到模板中。但是你可以先做一些技巧 你会像AfterViewInit这样ElementRef从 angular2 导入:

import {Component,AfterViewInit,ElementRef} from 'Angular2/core';

然后你会像这样在你的类中实现它们:

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
 }
}
于 2017-02-25T13:39:56.493 回答
1

我在组件初始化生命钩子中添加了动态加载js脚本的下一种方式:

    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);
}
于 2020-06-02T23:01:07.393 回答
0

绅士/绅士,请看一看。

对于添加像素脚本,这可能会节省您的时间,此解决方案比任何详细说明都容易:

- 仅适用于第一个加载页面 -

在您的 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>

对于在角度组件模板中使用脚本,我建议@Nicky's answer。

于 2021-11-30T09:07:26.417 回答
0
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);

这可以放在任何想要的地方,这是一个很好的方法

于 2019-09-20T12:15:51.827 回答