在 Angular 组件模板中添加脚本标签

IT技术 javascript angular json-ld angular2-universal
2021-01-27 07:24:00

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

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

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

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

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

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

6个回答

在这里参加聚会可能有点晚了,但由于上述答案不适用于 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);
    }
}
太好了,谢谢你解决这个问题。我只需要客户端实现
2021-03-15 07:24:00
Renderer2是不是一个内部服务直接使用。这就是为什么在上面的示例中,它setJsonLd作为参数而不是服务本身的依赖项发送给函数要解决此问题,Renderer2请先注入您的组件(而不是服务)。然后,在您的组件中注入服务。最后,调用服务功能setJsonLdRenderer2作为参数(在上述例子中,它是第一个参数)。希望这可以帮助!
2021-03-16 07:24:00
尝试Rendere2按照您的建议在服务中使用会引发错误:“没有 Renderer2 的提供者”。看到这个笨蛋
2021-03-20 07:24:00
我正在使用 Angular 2 并收到错误“Render2 未导出”。我需要在组件中动态地在 Angular 2 中添加模式。
2021-03-23 07:24:00
惊人的!我一直在寻找这个。Angular SSR 让人很头疼!我从不知道 renderer2 甚至存在:D
2021-03-31 07:24:00

没有将脚本标记添加到模板的 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 脚本

您能否发布指向该 Google SEO 推荐的链接?
2021-03-15 07:24:00
我在最初的问题中添加了一条评论,决定剥离标签而不是使它们变得惰性或以其他方式。为看一眼干杯!
2021-03-17 07:24:00
无论哪种方式,您都不能使用带有 JSON-LD 脚本标签的外部脚本,因为爬虫不会承认它。该脚本需要包含内联数据。在君特之前你没见过任何输出任意数据的方法吗?实际上,这种限制意味着没有 angular2 应用程序能够按照 Google 推荐的方式为 SEO 处理结构化数据。
2021-04-08 07:24:00
我会为这个用例创建一个错误报告。
2021-04-10 07:24:00
Angular 已经支持 JSON-LD 一段时间了,它不会删除这样的脚本标签。
2021-04-12 07:24:00

以下适用于 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);
    }
  }
我认为,使用粗箭头语法,您不需要_this = this访问类对象。你可以这样做,s.onload = ()=> { this.afterScriptAdded(); };
2021-04-06 07:24:00

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

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
 }
}
这是第一次有效,但如果您离开页面并返回,脚本不会重新触发。想法?
2021-04-13 07:24:00

我在组件 init life hook 中添加了动态加载的 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);
}