【问题标题】:Adding script tags in Angular component template在 Angular 组件模板中添加脚本标签
【发布时间】:2016-10-31 12:51:27
【问题描述】:

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 模板中包含 &lt;script&gt; 标签(即使标签在浏览器中是惰性的),或者这是框架过于固执己见的情况?如果这种情况在 angular2 中无法解决,还有哪些解决方案?

【问题讨论】:

标签: javascript angular json-ld angular2-universal


【解决方案1】:

绅士/绅士,请看一下。

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

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

在你的 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 的回答。

【讨论】:

    【解决方案2】:

    我在组件初始化生命钩子中添加了动态加载的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);
    }
    

    【讨论】:

      【解决方案3】:
      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);
      

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

      【讨论】:

        【解决方案4】:

        在这里聚会可能有点晚了,但由于上述答案不适用于 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);
            }
        }
        

        【讨论】:

        • 尝试按照您的建议在服务中使用Rendere2 会引发错误:“No provider for Renderer2”。见this plunk
        • Renderer2 不能在服务中直接使用。这就是为什么在上面的例子中它被作为参数发送到函数setJsonLd,而不是服务本身的依赖。要解决此问题,请先在您的组件中注入 Renderer2(而不是服务)。然后,将服务注入到您的组件中。最后以Renderer2为参数调用服务函数setJsonLd(上例中为第一个参数)。希望这会有所帮助!
        • 太好了,感谢您解决了这个问题。我只需要客户端实现
        • 太棒了!我一直在寻找这个年龄。 Angular SSR 让人头疼!我从来不知道渲染器2甚至存在:D
        • 2022 年 2 月来到这里的人? 谢谢@Nicky 的线索和很好的解释...新来的人我发现这篇不错的小文章详细解释了代码 sn-ps htmlgoodies.com/javascript/… 干杯 :-)
        【解决方案5】:

        以下适用于 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 = ()=&gt; { this.afterScriptAdded(); };
        【解决方案6】:

        没有 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: including thirdparty js scripts in component

        【讨论】:

        • 你不能使用带有 JSON-LD 脚本标签的外部脚本,因为爬虫不会承认它。该脚本需要包含内联数据。在 Günter 之前,您还没有见过 任何 种输出任意数据的方法吗?实际上,这种限制意味着任何 angular2 应用都无法按照 Google 推荐的方式为 SEO 处理结构化数据。
        • 您能否发布指向该 Google SEO 推荐的链接?
        • 我会为此用例创建一个错误报告。
        • 我已在原始问题中添加了一条评论,在该问题中决定strip 标签而不是使它们成为inert 或其他。干杯看看!
        • Angular 支持 JSON-LD 有一段时间了,它不会去除这样的脚本标签。
        【解决方案7】:

        其实 没有 Angular2 方法可以将 script 标签 添加到模板。 但你可以做一些技巧 首先您将从 angular2 导入 AfterViewInitElementRef 像这样:

        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
         }
        }
        

        【讨论】:

        • 这是第一次工作,但如果您离开页面并返回,脚本不会重新启动。想法?
        猜你喜欢
        • 2018-07-28
        • 2018-03-30
        • 1970-01-01
        • 2018-12-10
        • 2021-05-16
        • 2012-12-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多