【问题标题】:Angular 2 how to execute script tag from innerHTMLAngular 2如何从innerHTML执行脚本标签
【发布时间】:2017-01-09 11:54:25
【问题描述】:

我已经通过http.get() 方法加载了HTML 页面,并将此页面的内容添加到 div 标签中。

getRequestToAssignPage (param: string) : any {

    return this.$http.get(param)
        .map((res: Response) => {

            this.status = res;

            return res.text();
        })
        .toPromise()
        .then(response => {



            let restr: string = response;



            restr = restr.replace(/(<head[^>]*)(?:[^])*?\/head>/ig, '')
                .replace(/(<(\/?)body([^>]*?)>)/g, '')
                .replace(/(<style[^>]*)(?:[^])*?\/style>/g, '')
                .replace(/(<(\/?)html([^>]*?)>)/g, '')
                .replace(/(<app-root[^>]*)(?:[^])*?\/app-root>/ig, '')
                .replace(/(<\?[\s\S]*?\?>)|(<!DOCTYPE\s+\w+\s+\[[\s\S]*?\]>)|(<!\w[\s\S]*?>)/g, '')
                .replace(/(href\s*=\s*(?:"))/ig, 'href="/#')
                .replace(/(href\s*=\s*(?:'))/ig, "href='/#");



            this.response = restr;


        })
        .catch(error => this.status = error );

}

你怎么看,这个方法,把response放到变量里,用正则表达式解析字符串 好的,接下来我把它添加到div中,像这样

<div [innerHTML]="response | safe"></div>

好的,我的页面正在显示。但是,脚本不起作用。它们存在于 div 标签中,但不执行。

我曾尝试使用 eval() 这样做,但结果很糟糕

let scripts: string[] = restr.match(/\<scr[\s\S]*?ipt>/g);

            this.srcfield.nativeElement.innerHTML = '';

            scripts.forEach((value, index) => {
                eval.call(null, (this.srcfield.nativeElement.innerHTML = value));
            });

SyntaxError: Unexpected token

为什么innerHTML 不执行加载的脚本标签?我该如何解决?

【问题讨论】:

    标签: javascript http angular


    【解决方案1】:

    基于 Adam 的解决方案,您可以实现自定义指令以及管道并将脚本重新插入 DOM。这将考虑两种情况:内联脚本和“src”脚本。请记住,允许这样的脚本是非常危险的。

    管道:

    import { Pipe, PipeTransform } from '@angular/core';
    import { DomSanitizer } from '@angular/platform-browser';
    
    @Pipe({ name: 'safeHtml' })
    export class SafeHtmlPipe implements PipeTransform {
        constructor(private sanitizer: DomSanitizer) { }
    
        transform(html) {
            return this.sanitizer.bypassSecurityTrustHtml(html);
        }
    }
    

    指令:

    import { Directive, ElementRef, OnInit } from '@angular/core';
    
    @Directive({ selector: '[runScripts]' })
    export class RunScriptsDirective implements OnInit {
        constructor(private elementRef: ElementRef) { }
        ngOnInit(): void {
            setTimeout(() => { // wait for DOM rendering
                this.reinsertScripts();
            });
        }
        reinsertScripts(): void {
            const scripts = <HTMLScriptElement[]>this.elementRef.nativeElement.getElementsByTagName('script');
            const scriptsInitialLength = scripts.length;
            for (let i = 0; i < scriptsInitialLength; i++) {
                const script = scripts[i];
                const scriptCopy = <HTMLScriptElement>document.createElement('script');
                scriptCopy.type = script.type ? script.type : 'text/javascript';
                if (script.innerHTML) {
                    scriptCopy.innerHTML = script.innerHTML;
                } else if (script.src) {
                    scriptCopy.src = script.src;
                }
                scriptCopy.async = false;
                script.parentNode.replaceChild(scriptCopy, script);
            }
        }
    }
    

    用法:

    <div [innerHTML]="myDynamicMarkup | safeHtml" runScripts></div>
    

    【讨论】:

    • 这正是我所需要的,但我的脚本使用了 document.write,我猜这样执行时它不会工作,我无法更改脚本..
    • 在我的情况下,runScript 指令在数据加载到 [innerHtml] 之前运行,因此不会提取脚本。有什么想法吗?
    • @WalterLuszczyk 我知道你的问题很久以前就被问过了,但如果其他人有同样的问题,我会提供 2 条建议。首先,您可以随时调整“setTimeout()”中的毫秒数。但更好的解决方案是在您的元素上放置一个 *ngIf,以便它仅在数据存在时呈现。这应该强制指令在数据已经加载后运行。
    【解决方案2】:

    在您的模板中,您可以设置如下内容:

    <div #htmlContainer [innerHTML]="trustedHTML"></div>
    

    在相应的组件中,你可以将你的 HTML 加载到你的模板中,构建一个脚本标签数组,并 eval 一个:

    @ViewChild('htmlContainer') container;
    
    ngOnInit() {
      this.http.get('html-file.html').subscribe((res: Response) => {
        this.trustedHTML = this.sanitizer.bypassSecurityTrustHtml(res.text());
    
        setTimeout(() => { //wait for DOM rendering
          let scripts = this.container.nativeElement.getElementsByTagName('script');
          for (let script of scripts){
            eval(script.text)
          }
        })
    
      });
    

    我讨厌我不得不使用setTimeouteval 来完成看似简单但确实有效的事情。

    ...但不适用于&lt;script src="...。我最终导入了 jQuery 并使用了$(parent).load(url);。如果有人有纯 Angular 解决方案,请发布。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-12-23
      • 1970-01-01
      • 1970-01-01
      • 2016-09-17
      • 1970-01-01
      • 2019-07-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多