web-dev-qa-db-ja.com

Angular 2で$ compileと同等

HTMLを含むディレクティブを手動でコンパイルしたいのですが。 Angular 2の$compileと同等のものは何ですか?

たとえば、Angular 1では、HTMLのフラグメントを動的にコンパイルしてDOMに追加することができます。

var e = angular.element('<div directive></div>');
element.append(e);
$compile(e)($scope);
121
pixelbits

アンギュラ 2.3. (2016-12-07)

詳細をすべて確認するには

実際に動作していることを確認するには

プリンシパル:

1)テンプレートを作成する
2)コンポーネントを作成する
3)モジュールを作成する
4)コンパイルモジュール
5)ComponentFactoryを作成する(そしてキャッシュする)
6)Targetを使用してそのインスタンスを作成します

コンポーネントの作成方法の概要

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

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

ComponentFactoryを作成する(そしてそれをキャッシュする)コードスニペット

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

上記の結果を使用する方法のコードスニペット

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

すべての詳細を含む完全な説明 ここで読む 、または観察 実施例

OBSOLETE - Angular 2.0 RC5関連(RC5のみ)

以前のRCバージョンに対する以前の解決策を見るには、 この記事の歴史を検索してください。

125
Radim Köhler

注:@BennyBottemaがコメントで述べているように、DynamicComponentLoaderは現在推奨されていません。したがって、この回答もそうです。


Angular2には、 $ compile 同等のものはありません。 DynamicComoponentLoader を使ってES6クラスをハックすると、コードを動的にコンパイルできます( 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'
    );
  }
}

しかし、それはhtmlパーサーがangular2コアの内側になるまでしか働かないでしょう。

33
alexpods

Angular Versionを使用しました - Angular 4.2.0

Angular 4は、実行時にコンポーネントをロードするためにComponentFactoryResolverを思い付きました。これはAngular 1.0の$ compileの一種の同じ実装で、これはあなたの必要性を満たすものです。

以下の例では、ImageWidgetコンポーネントを動的にDashboardTileComponentにロードしています。

コンポーネントをロードするには、動的コンポーネントを配置するのに役立つng-templateに適用できるディレクティブが必要です。

WidgetHostDirective

 import { Directive, ViewContainerRef } from '@angular/core';

    @Directive({
      selector: '[widget-Host]',
    })
    export class DashboardTileWidgetHostDirective {
      constructor(public viewContainerRef: ViewContainerRef) { 


      }
    }

このディレクティブは、ViewContainerRefを挿入して、動的に追加されたコンポーネントをホストする要素のビューコンテナにアクセスします。

DashboardTileComponent(動的コンポーネントを描画するためのプレースホルダコンポーネント)

このコンポーネントは、親コンポーネントからの入力を受け入れるか、実装に基づいてサービスからロードできます。このコンポーネントは、実行時にコンポーネントを解決するための主要な役割を果たしています。このメソッドでは、renderComponent()という名前のメソッドも見ることができます。これは最終的にサービスからコンポーネント名をロードし、ComponentFactoryResolverで解決しますそして最後にデータを動的コンポーネントに設定します。

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

これは動的に解決したいすべてのコンポーネントを登録するサービスファクトリです。

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

ImageTextWidgetComponent(実行時にロードしているコンポーネント)

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() { }
}

追加最後にこのImageTextWidgetComponentを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;
    }

私のブログからの元の参照

公式文書

サンプルソースコードのダウンロード

15
Code-EZ

このnpmパッケージは私にとってそれを容易にしました: https://www.npmjs.com/package/ngx-dynamic-template

使用法:

<ng-template dynamic-template
             [template]="'some value:{{param1}}, and some component <lazy-component></lazy-component>'"
             [context]="{param1:'value1'}"
             [extraModules]="[someDynamicModule]"></ng-template>
8
aelbatal

Angular TypeScript/ES6(Angular 2+)

AOT + JITと同時に動作します。

私はここでそれを使用する方法を作成しました: https://github.com/patrikx3/angular-compile

npm install p3x-angular-compile

コンポーネント:コンテキストといくつかのHTMLデータを持っている必要があります...

HTML:

<div [p3x-compile]="data" [p3x-compile-context]="ctx">loading ...</div>
3
Patrik Laszlo

コンポーネントのインスタンスを動的に作成してDOMに添付するには、次のスクリプトを使用できます。Angular RCで動作するはずです。

htmlテンプレート:

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

ローダーコンポーネント

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

}
3
fabio_biondi