Equivalent of $compile in Angular 2

后端 未结 9 792
名媛妹妹
名媛妹妹 2020-11-21 06:50

I want to manually compile some HTML containing directives. What is the equivalent of $compile in Angular 2?

For example, in Angular 1, I could dynamic

相关标签:
9条回答
  • 2020-11-21 07:01

    If you want to inject html code use directive

    <div [innerHtml]="htmlVar"></div>
    

    If you want to load whole component in some place, use DynamicComponentLoader:

    https://angular.io/docs/ts/latest/api/core/DynamicComponentLoader-class.html

    0 讨论(0)
  • 2020-11-21 07:03

    Note: As @BennyBottema mentions in a comment, DynamicComponentLoader is now deprecated, hence so is this answer.


    Angular2 doesn't have any $compile equivalent. You can use DynamicComoponentLoader and hack with ES6 classes to compile your code dynamically (see this plunk):

    import {Component, DynamicComponentLoader, ElementRef, OnInit} from 'angular2/core'
    
    function compileToComponent(template, directives) {
      @Component({ 
        selector: 'fake', 
        template , directives
      })
      class FakeComponent {};
      return FakeComponent;
    }
    
    @Component({
      selector: 'hello',
      template: '<h1>Hello, Angular!</h1>'
    })
    class Hello {}
    
    @Component({
      selector: 'my-app',
      template: '<div #container></div>',
    })
    export class App implements OnInit {
      constructor(
        private loader: DynamicComponentLoader, 
        private elementRef: ElementRef,
      ) {}
    
      ngOnInit() {} {
        const someDynamicHtml = `<hello></hello><h2>${Date.now()}</h2>`;
    
        this.loader.loadIntoLocation(
          compileToComponent(someDynamicHtml, [Hello])
          this.elementRef,
          'container'
        );
      }
    }
    

    But it will work only until html parser is inside angular2 core.

    0 讨论(0)
  • 2020-11-21 07:04

    In order to dinamically create an instance of a component and attach it to your DOM you can use the following script and should work in Angular RC:

    html template:

    <div>
      <div id="container"></div>
      <button (click)="viewMeteo()">Meteo</button>
      <button (click)="viewStats()">Stats</button>
    </div>
    

    Loader component

    import { Component, DynamicComponentLoader, ElementRef, Injector } from '@angular/core';
    import { WidgetMeteoComponent } from './widget-meteo';
    import { WidgetStatComponent } from './widget-stat';
    
    @Component({
      moduleId: module.id,
      selector: 'widget-loader',
      templateUrl: 'widget-loader.html',
    })
    export class WidgetLoaderComponent  {
    
      constructor( elementRef: ElementRef,
                   public dcl:DynamicComponentLoader,
                   public injector: Injector) { }
    
      viewMeteo() {
        this.dcl.loadAsRoot(WidgetMeteoComponent, '#container', this.injector);
      }
    
      viewStats() {
        this.dcl.loadAsRoot(WidgetStatComponent, '#container', this.injector);
      }
    
    }
    
    0 讨论(0)
  • 2020-11-21 07:12

    Angular 2.3.0 (2016-12-07)

    To get all the details check:

    • How can I use/create dynamic template to compile dynamic Component with Angular 2.0?

    To see that in action:

    • observe a working plunker (working with 2.3.0+)

    The principals:

    1) Create Template
    2) Create Component
    3) Create Module
    4) Compile Module
    5) Create (and cache) ComponentFactory
    6) use Target to create an Instance of it

    A quick overview how to create a Component

    createNewComponent (tmpl:string) {
      @Component({
          selector: 'dynamic-component',
          template: tmpl,
      })
      class CustomDynamicComponent  implements IHaveDynamicData {
          @Input()  public entity: any;
      };
      // a component for this particular template
      return CustomDynamicComponent;
    }
    

    A way how to inject component into NgModule

    createComponentModule (componentType: any) {
      @NgModule({
        imports: [
          PartsModule, // there are 'text-editor', 'string-editor'...
        ],
        declarations: [
          componentType
        ],
      })
      class RuntimeComponentModule
      {
      }
      // a module for just this Type
      return RuntimeComponentModule;
    }
    

    A code snippet how to create a ComponentFactory (and cache it)

    public createComponentFactory(template: string)
        : Promise<ComponentFactory<IHaveDynamicData>> {    
        let factory = this._cacheOfFactories[template];
    
        if (factory) {
            console.log("Module and Type are returned from cache")
    
            return new Promise((resolve) => {
                resolve(factory);
            });
        }
    
        // unknown template ... let's create a Type for it
        let type   = this.createNewComponent(template);
        let module = this.createComponentModule(type);
    
        return new Promise((resolve) => {
            this.compiler
                .compileModuleAndAllComponentsAsync(module)
                .then((moduleWithFactories) =>
                {
                    factory = _.find(moduleWithFactories.componentFactories
                                    , { componentType: type });
    
                    this._cacheOfFactories[template] = factory;
    
                    resolve(factory);
                });
        });
    }
    

    A code snippet how to use the above result

      // here we get Factory (just compiled or from cache)
      this.typeBuilder
          .createComponentFactory(template)
          .then((factory: ComponentFactory<IHaveDynamicData>) =>
        {
            // Target will instantiate and inject component (we'll keep reference to it)
            this.componentRef = this
                .dynamicComponentTarget
                .createComponent(factory);
    
            // let's inject @Inputs to component instance
            let component = this.componentRef.instance;
    
            component.entity = this.entity;
            //...
        });
    

    The full description with all the details read here, or observe working example

    .

    .

    OBSOLETE - Angular 2.0 RC5 related (RC5 only)

    to see previous solutions for previous RC versions, please, search through the history of this post

    0 讨论(0)
  • 2020-11-21 07:13

    Angular Version I have Used - Angular 4.2.0

    Angular 4 is came up with ComponentFactoryResolver to load components at runtime. This is a kind of same implementation of $compile in Angular 1.0 which serves your need

    In this below example I am loading ImageWidget component dynamically in to a DashboardTileComponent

    In order to load a component you need a directive that you can apply to ng-template which will helps to place the dynamic component

    WidgetHostDirective

     import { Directive, ViewContainerRef } from '@angular/core';
    
        @Directive({
          selector: '[widget-host]',
        })
        export class DashboardTileWidgetHostDirective {
          constructor(public viewContainerRef: ViewContainerRef) { 
    
    
          }
        }
    

    this directive injects ViewContainerRef to gain access to the view container of the element that will host the dynamically added component.

    DashboardTileComponent(Place holder component to render the dynamic component)

    This component accepts an input which is coming from a parent components or you can load from your service based on your implementation. This component is doing the major role to resolve the components at runtime. In this method you can also see a method named renderComponent() which ultimately loads the component name from a service and resolve with ComponentFactoryResolver and finally setting data to the dynamic component.

    import { Component, Input, OnInit, AfterViewInit, ViewChild, ComponentFactoryResolver, OnDestroy } from '@angular/core';
    import { DashboardTileWidgetHostDirective } from './DashbardWidgetHost.Directive';
    import { TileModel } from './Tile.Model';
    import { WidgetComponentService } from "./WidgetComponent.Service";
    
    
    @Component({
        selector: 'dashboard-tile',
        templateUrl: 'app/tile/DashboardTile.Template.html'
    })
    
    export class DashboardTileComponent implements OnInit {
        @Input() tile: any;
        @ViewChild(DashboardTileWidgetHostDirective) widgetHost: DashboardTileWidgetHostDirective;
        constructor(private _componentFactoryResolver: ComponentFactoryResolver,private widgetComponentService:WidgetComponentService) {
    
        }
    
        ngOnInit() {
    
        }
        ngAfterViewInit() {
            this.renderComponents();
        }
        renderComponents() {
            let component=this.widgetComponentService.getComponent(this.tile.componentName);
            let componentFactory = this._componentFactoryResolver.resolveComponentFactory(component);
            let viewContainerRef = this.widgetHost.viewContainerRef;
            let componentRef = viewContainerRef.createComponent(componentFactory);
            (<TileModel>componentRef.instance).data = this.tile;
    
        }
    }
    

    DashboardTileComponent.html

     <div class="col-md-2 col-lg-2 col-sm-2 col-default-margin col-default">        
                            <ng-template widget-host></ng-template>
    
              </div>
    

    WidgetComponentService

    This is a service factory to register all the components that you want to resolve dynamically

    import { Injectable }           from '@angular/core';
    import { ImageTextWidgetComponent } from "../templates/ImageTextWidget.Component";
    @Injectable()
    export class WidgetComponentService {
      getComponent(componentName:string) {
              if(componentName==="ImageTextWidgetComponent"){
                  return ImageTextWidgetComponent
              }
      }
    }
    

    ImageTextWidgetComponent(component we are loading at runtime)

    import { Component, OnInit, Input } from '@angular/core';
    
    
    @Component({
        selector: 'dashboard-imagetextwidget',
        templateUrl: 'app/templates/ImageTextWidget.html'
    })
    
    export class ImageTextWidgetComponent implements OnInit {
         @Input() data: any;
        constructor() { }
    
        ngOnInit() { }
    }
    

    Add Finally add this ImageTextWidgetComponent in to your app module as entryComponent

    @NgModule({
        imports: [BrowserModule],
        providers: [WidgetComponentService],
        declarations: [
            MainApplicationComponent,
            DashboardHostComponent,
            DashboardGroupComponent,
            DashboardTileComponent,
            DashboardTileWidgetHostDirective,
            ImageTextWidgetComponent
            ],
        exports: [],
        entryComponents: [ImageTextWidgetComponent],
        bootstrap: [MainApplicationComponent]
    })
    export class DashboardModule {
        constructor() {
    
        }
    }
    

    TileModel

     export interface TileModel {
          data: any;
        }
    

    Orginal Reference from my blog

    Official Documentation

    Download Sample Source Code

    0 讨论(0)
  • 2020-11-21 07:13

    You can see the component, that allow to compile simple dynamic Angular components https://www.npmjs.com/package/@codehint-ng/html-compiler

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