Angular 2 equivalent of ng-bind-html, $sce.trustAsHTML(), and $compile?

前端 未结 6 913
暖寄归人
暖寄归人 2020-11-30 09:46

In Angular 1.x, we could insert HTML in real-time by using the HTML tag ng-bind-html, combined with the JavaScript call $sce.trustAsHTML(). This go

相关标签:
6条回答
  • 2020-11-30 10:26

    Have a look at this module https://www.npmjs.com/package/ngx-dynamic-template

    After a long research, only this thing helped me. The rest of the solutions seems to be outdated.

    0 讨论(0)
  • 2020-11-30 10:27

    DynamicComponentLoader is deprecated, you can use ComponentResolver instead

    You could use this directive, add pipes if you need additional data manipulation. It also allows for lazy loading, you don't need it in your case, but it's worth mentioning.

    Directive(I found this code and made some changes, you can do that too to make it fit your taste or use it as is):

    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, []);
          });
      }
    }
    

    Example how to use it:

    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>
    
    0 讨论(0)
  • 2020-11-30 10:27

    Angular provided DynamicComponentLoader class for loading html dynamically. DynamicComponentLoader have methods for inserting components. loadIntoLocation is one of them for inserting component.

    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>
    

    Few things you need to take care of :

    • Don't call loadIntoLocation inside the constructor of class . Component view is not yet created when component constructor is called. You will get error -

    Error during instantiation of AppComponent!. There is no component directive at element [object Object]

    • Put anchorName #child in html otherwise you will get error.

    Could not find variable child

    0 讨论(0)
  • 2020-11-30 10:37

    I think all you have to do is set the element you want to have compiled html with the [innerHTML]="yourcomponentscopevar"

    0 讨论(0)
  • 2020-11-30 10:39

    After reading a lot, and being close of opening a new topic I decided to answer here just to try to help to others. As I've seen there are several changes with the latest version of Angular 2. (Currently Beta9)

    I'll try to share my code in order to avoid the same frustration I had...

    First, in our index.html

    As usual, we should have something like this:

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

    AppComponent (using innerHTML)

    With this property you will be able to render the basic HTML, but you won't be able to do something similar to Angular 1.x as $compile through a scope:

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

    This will render the following:

    Hello my Interpolated: Angular 2 app!

    Hello my Property bound: Angular 2 app!

    Hello my Interpolated: {{title}}

    AppComponent Using DynamicComponentLoader

    There is a little bug with the docs, documented in here. So if we have in mind that, my code should look now like this:

    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');
        }
    }
    

    This will render the following:

    Hello my Interpolated: Angular 2 app!

    Hello my Property bound: Angular 2 app!

    Hello my Property bound: ChildComponent title

    Hello my Interpolated: ChildComponent title

    End of parent: Bye bye!

    0 讨论(0)
  • 2020-11-30 10:48

    In Angular2 you should use DynamicComponentLoader to insert some "compiled content" on the page. So for example if you want to compile next html:

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

    then you need to create component with this html as a template (let's call it CompiledComponent) and use DynamicComponentLoader to insert this component on the page.

    @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');
      }
    }
    

    Check out this plunker

    UPD You can create component dynamically right before the loader.loadIntoLocation() call:

    // ... 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');
      }
    }
    

    I personally don't like it, it's look like a dirty hack to me. But here is the plunker

    PS Beware that at this moment angular2 is under active development. So situation can be changed at any time.

    0 讨论(0)
提交回复
热议问题