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

Ian*_*her 40 javascript json-ld angular2-universal angular

Angular2 <script>自动从模板中删除标签,以阻止人们使用此功能作为"穷人"加载器.

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

目前使用的是JSON-LD,它采用的格式

<script type="application/ld+json">
{
    "@context":"http://schema.org",
    "@type":"HealthClub",
    ...
}
</script>
Run Code Online (Sandbox Code Playgroud)

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

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

Nic*_*cky 40

也许这里的派对有点晚了,但由于上述答案与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);
    }
}
Run Code Online (Sandbox Code Playgroud)

服务实施

注意:服务不能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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 惊人的!我一直在寻找这个。Angular SSR 让人很头疼!我从不知道 renderer2 甚至存在:D (2认同)
  • 2022 年 2 月这里的人们感谢 @Nicky 的引导和很好的解释...新来者我发现这篇不错的小文章用代码片段详细解释了 https://www.htmlgoodies.com/javascript/loading-external-scripts-动态角度/干杯:-) (2认同)

Gün*_*uer 17

没有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);
}
Run Code Online (Sandbox Code Playgroud)

另请参见angular2:包括组件中的thirdparty js脚本

  • 您不能将外部脚本与JSON-LD脚本标记一起使用,因为爬虫不会对其进行确认.该脚本需要包含内联数据.在Günter之前你还没有看到任何输出任意数据的方法?实际上,这种限制意味着没有angular2 app能够以Google推荐的方式为SEO做结构化数据. (3认同)

Ket*_*ale 12

以下适用于Angular 5.2.7:

所需的进口是:

import { Inject, AfterViewInit, ElementRef } from '@angular/core';
import { DOCUMENT } from '@angular/common';
Run Code Online (Sandbox Code Playgroud)

实现AfterViewInit:

export class HeroesComponent implements AfterViewInit {
Run Code Online (Sandbox Code Playgroud)

如果您的组件正在实现更多的接口,请用逗号分隔它们; 例如:

export class HeroesComponent implements OnInit, AfterViewInit {
Run Code Online (Sandbox Code Playgroud)

将以下参数传递给构造函数:

constructor(@Inject(DOCUMENT) private document, private elementRef: ElementRef) { }
Run Code Online (Sandbox Code Playgroud)

添加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);
  }
Run Code Online (Sandbox Code Playgroud)

添加afterScriptAdded成员函数.

成功加载外部脚本后将调用此函数.因此,您要在外部js中使用的属性或函数将在此函数的主体中访问.

 afterScriptAdded() {
    const params= {
      width: '350px',
      height: '420px',
    };
    if (typeof (window['functionFromExternalScript']) === 'function') {
      window['functionFromExternalScript'](params);
    }
  }
Run Code Online (Sandbox Code Playgroud)

  • 我认为,使用粗箭头语法,您不需要“_this = this”来访问类对象。你可以这样做, `s.onload = ()=&gt; { this.afterScriptAdded(); };` (2认同)

小智 5

实际上 没有Angular2方法向模板添加脚本标记.但是你可以先做一些技巧 ,AfterViewInit然后ElementRef从angular2 导入,就像这样:

import {Component,AfterViewInit,ElementRef} from 'Angular2/core';
Run Code Online (Sandbox Code Playgroud)

那么你将在你的班级中实现它们:

export class example1 implements AfterViewInit{}
Run Code Online (Sandbox Code Playgroud)

这是一个非常简单的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
 }
}
Run Code Online (Sandbox Code Playgroud)