【问题标题】:Angular 2 equivalent of ng-bind-html, $sce.trustAsHTML(), and $compile?Angular 2 相当于 ng-bind-html、$sce.trustAsHTML() 和 $compile?
【发布时间】:2015-11-27 05:21:17
【问题描述】:

在 Angular 1.x 中,我们可以通过使用 HTML 标签 ng-bind-html 结合 JavaScript 调用 $sce.trustAsHTML() 来实时插入 HTML。这让我们完成了 80% 的工作,但在使用 Angular 标记时不起作用,例如如果您插入使用 ng-repeat 或自定义指令的 HTML。

要让它发挥作用,我们可以使用custom directive that called $compile

Angular 2 中所有这些的等价物是什么?我们可以使用[inner-html] 进行绑定,但这仅适用于非常简单的HTML 标签,例如<b>。它不会将自定义 angular 2 指令转换为功能性 HTML 元素。 (很像没有 $compile 步骤的 Angular 1.x。)对于 Angular 2,$compile 的等价物是什么?

【问题讨论】:

    标签: innerhtml angular


    【解决方案1】:

    在 Angular2 中,您应该使用DynamicComponentLoader 在页面上插入一些“编译内容”。所以例如如果你想编译下一个 html:

    <div>
        <p>Common HTML tag</p>
        <angular2-component>Some angular2 component</angular2-component>
    </div>
    

    那么你需要用这个html作为模板创建组件(我们称之为CompiledComponent)并使用DynamicComponentLoader在页面上插入这个组件。

    @Component({
      selector: 'compiled-component'
    })
    @View({
      directives: [Angular2Component],
      template: `
        <div>
          <p>Common HTML tag</p>
          <angular2-component>Angular 2 component</angular2-component>
        </div>
      `
    })
    class CompiledComponent {
    }
    
    @Component({
      selector: 'app'
    })
    @View({
      template: `
        <h2>Before container</h2>
        <div #container></div>
        <h2>After conainer</h2>
      `
    })
    class App {
      constructor(loader: DynamicComponentLoader, elementRef: ElementRef) {
        loader.loadIntoLocation(CompiledComponent, elementRef, 'container');
      }
    }
    

    查看this plunker

    UPD 您可以在 loader.loadIntoLocation() 调用之前动态创建组件:

    // ... annotations
    class App {
      constructor(loader: DynamicComponentLoader, elementRef: ElementRef) {
        // template generation
        const generatedTemplate = `<b>${Math.random()}</b>`;
    
        @Component({ selector: 'compiled-component' })
        @View({ template: generatedTemplate })
        class CompiledComponent {};
    
        loader.loadIntoLocation(CompiledComponent, elementRef, 'container');
      }
    }
    

    我个人不喜欢它,对我来说它看起来像一个肮脏的黑客。但这里是the plunker

    PS 请注意,此时 angular2 正在积极开发中。所以情况可以随时改变。

    【讨论】:

    • 是否可以在运行时使用 JavaScript 更改模板的 HTML?我事先不知道 HTML 是否能够将它输入到模板中……它是动态计算的。
    • AFAIK 您不能更改组件装饰器的属性。这不是一个好主意。
    • 因此,想象一下随着时间的推移您有 json 从服务器返回的场景。您想显示这两个 json 响应之间的差异,并根据是否添加或删除了行来为绿色或红色注入一些 html 格式。据您所知,这不是 Angular2 中受支持的用例,因为模板是动态的?
    • 对于我的用例,我能够在返回的 html 中添加一个样式标签。它不漂亮,但现在可以使用。
    • (浪费了2小时),解决方法很简单,请查看stackoverflow.com/questions/31548311/…
    【解决方案2】:

    DynamicComponentLoader 已弃用,您可以改用 ComponentResolver

    你可以使用这个指令,如果你需要额外的数据操作,可以添加管道。它还允许延迟加载,在您的情况下不需要它,但值得一提。

    指令(我找到了这段代码并做了一些更改,您也可以这样做以使其符合您的口味或按原样使用):

    import { Component, Directive, ComponentFactory, ComponentMetadata, ComponentResolver, Input, ReflectiveInjector, ViewContainerRef } from '@angular/core';
    declare var $:any;
    
    export function createComponentFactory(resolver: ComponentResolver, metadata: ComponentMetadata): Promise<ComponentFactory<any>> {
        const cmpClass = class DynamicComponent {};
        const decoratedCmp = Component(metadata)(cmpClass);
        return resolver.resolveComponent(decoratedCmp);
    }
    
    @Directive({
        selector: 'dynamic-html-outlet',
    })
    export class DynamicHTMLOutlet {
      @Input() htmlPath: string;
      @Input() cssPath: string;
    
      constructor(private vcRef: ViewContainerRef, private resolver: ComponentResolver) {
      }
    
      ngOnChanges() {
        if (!this.htmlPath) return;
        $('dynamic-html') && $('dynamic-html').remove();
        const metadata = new ComponentMetadata({
            selector: 'dynamic-html',
            templateUrl: this.htmlPath +'.html',
            styleUrls:  [this.cssPath]
        });
        createComponentFactory(this.resolver, metadata)
          .then(factory => {
            const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector);
            this.vcRef.createComponent(factory, 0, injector, []);
          });
      }
    }
    

    使用示例:

    import { Component, OnInit } from '@angular/core';
    import { DynamicHTMLOutlet } from './../../directives/dynamic-html-outlet/dynamicHtmlOutlet.directive';
    
    @Component({
      selector: 'lib-home',
      templateUrl: './app/content/home/home.component.html',
      directives: [DynamicHTMLOutlet]
    })
    export class HomeComponent implements OnInit{
        html: string;
        css: string;
    
        constructor() {}
    
        ngOnInit(){
        this.html = './app/content/home/home.someTemplate.html';
        this.css = './app/content/home/home.component.css';
        }
    
      }
    

    home.component.html:

    <dynamic-html-outlet [htmlPath]="html" [cssPath]="css"></dynamic-html-outlet>
    

    【讨论】:

    • 嗨...上面的解决方案给出了如下错误,Typescript 错误模块 '"D:/app/node_modules/@angular/core/core"' 没有导出的成员 'ComponentResolver'。 Typescript 错误模块 '"D:/app/node_modules/@angular/core/core"' 没有导出的成员 'ComponentMetadata'。
    【解决方案3】:

    在阅读了很多之后,并且即将打开一个新主题,我决定在这里回答,只是为了帮助别人。正如我所见,最新版本的 Angular 2 有一些变化。(目前是 Beta9)

    我将尝试分享我的代码以避免同样的挫败感...

    首先,在我们的 index.html 中

    像往常一样,我们应该有这样的东西:

    <html>
     ****
      <body>
        <my-app>Loading...</my-app>
      </body>
    </html>
    

    AppComponent(使用innerHTML)

    有了这个属性,你就可以渲染基本的 HTML,但是你不能像 Angular 1.x 那样通过一个作用域做 $compile:

    import {Component} from 'angular2/core';
    
    @Component({
        selector: 'my-app',
        template: `
                    <h1>Hello my Interpolated: {{title}}!</h1>
                    <h1 [textContent]="'Hello my Property bound: '+title+'!'"></h1>
                    <div [innerHTML]="htmlExample"></div>
                 `,
    })
    
    export class AppComponent {
        public title = 'Angular 2 app';
        public htmlExample = '  <div>' +
                                    '<span [textContent]="\'Hello my Property bound: \'+title"></span>' +
                                    '<span>Hello my Interpolated: {{title}}</span>' +
                                '</div>'
    }
    

    这将呈现以下内容:

    你好我的插值:Angular 2 应用程序!

    你好我的属性绑定:Angular 2 应用程序!

    你好我的插值:{{title}}

    AppComponent 使用DynamicComponentLoader

    文档中有一个小错误,记录在 here 中。因此,如果我们考虑到这一点,我的代码现在应该如下所示:

    import {DynamicComponentLoader, Injector, Component, ElementRef, OnInit} from "angular2/core";
    
    @Component({
        selector: 'child-component',
        template: `
            <div>
                <h2 [textContent]="'Hello my Property bound: '+title"></h2>
                <h2>Hello my Interpolated: {{title}}</h2>
            </div>
        `
    })
    class ChildComponent {
         title = 'ChildComponent title';
    }
    
    @Component({
        selector: 'my-app',
        template: `
                    <h1>Hello my Interpolated: {{title}}!</h1>
                    <h1 [textContent]="'Hello my Property bound: '+title+'!'"></h1>
                    <div #child></div>
                    <h1>End of parent: {{endTitle}}</h1>
                 `,
    })
    
    export class AppComponent implements OnInit{
        public title = 'Angular 2 app';
        public endTitle= 'Bye bye!';
    
        constructor(private dynamicComponentLoader:DynamicComponentLoader, private elementRef: ElementRef) {
    //        dynamicComponentLoader.loadIntoLocation(ChildComponent, elementRef, 'child');
        }
    
        ngOnInit():any {
            this.dynamicComponentLoader.loadIntoLocation(ChildComponent, this.elementRef, 'child');
        }
    }
    

    这将呈现以下内容:

    你好我的插值:Angular 2 应用程序!

    你好我的属性绑定:Angular 2 应用程序!

    你好我的属性绑定:子组件标题

    你好我的插值:子组件标题

    父母结束:再见!

    【讨论】:

      【解决方案4】:

      我认为您所要做的就是使用 [innerHTML]="yourcomponentscopevar" 设置要编译 html 的元素

      【讨论】:

      【解决方案5】:

      Angular 提供了 DynamicComponentLoader 类用于动态加载 html。 DynamicComponentLoader 有插入组件的方法。 loadIntoLocation 是其中之一,用于插入组件。

      paper.component.ts

      import {Component,DynamicComponentLoader,ElementRef,Inject,OnInit} from 'angular2/core';
      import { BulletinComponent } from './bulletin.component';
      
      @Component({
          selector: 'paper',
          templateUrl: 'app/views/paper.html'
      
          }
      })
      export class PaperComponent {
          constructor(private dynamicComponentLoader:DynamicComponentLoader, private elementRef: ElementRef) {
      
          }
      
          ngOnInit(){
              this.dynamicComponentLoader.loadIntoLocation(BulletinComponent, this.elementRef,'child');
      
          }
      }
      

      bulletin.component.ts

      import {Component} from 'angular2/core';
      
      @Component({
          selector: 'bulletin',
          templateUrl: 'app/views/bulletin.html'
          }
      })
      export class BulletinComponent {}
      

      paper.html

      <div>
          <div #child></div>
      </div>
      

      您需要注意的几件事:

      • 不要在 class 的构造函数中调用loadIntoLocation。调用组件构造函数时尚未创建组件视图。你会得到错误 -

      AppComponent! 实例化期间出错。没有组件 元素 [object Object] 处的指令

      • 将anchorName #child 放在html 中,否则会出错。

      找不到变量子

      【讨论】:

        【解决方案6】:

        看看这个模块https://www.npmjs.com/package/ngx-dynamic-template

        经过长时间的研究,只有这件事对我有帮助。其余的解决方案似乎已经过时了。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-02-08
          • 1970-01-01
          • 1970-01-01
          • 2013-08-22
          • 1970-01-01
          • 2018-05-31
          • 2013-12-23
          • 2017-02-22
          相关资源
          最近更新 更多