HTMLを含むディレクティブを手動でコンパイルしたいのですが。 Angular 2の$compile
と同等のものは何ですか?
たとえば、Angular 1では、HTMLのフラグメントを動的にコンパイルしてDOMに追加することができます。
var e = angular.element('<div directive></div>');
element.append(e);
$compile(e)($scope);
詳細をすべて確認するには
実際に動作していることを確認するには
プリンシパル:
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バージョンに対する以前の解決策を見るには、 この記事の歴史を検索してください。
注:@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コアの内側になるまでしか働かないでしょう。
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;
}
この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>
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>
コンポーネントのインスタンスを動的に作成して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);
}
}