219

テンプレートを動的に作成したい。ComponentTypeこれは、実行時に を構築し、ホスティング コンポーネント内のどこかに配置(置換も)するために使用する必要があります。

RC4 までは を使用してComponentResolverいましたが、RC5 では次のメッセージが表示されます。

ComponentResolver is deprecated for dynamic compilation.
Use ComponentFactoryResolver together with @NgModule/@Component.entryComponents or ANALYZE_FOR_ENTRY_COMPONENTS provider instead.
For runtime compile only, you can also use Compiler.compileComponentSync/Async.

このドキュメントを見つけました ( Angular 2 Synchronous Dynamic Component Creation )

そして、私はどちらも使用できることを理解してください

  • ngIfとダイナミックな種類ComponentFactoryResolver。内部で既知のコンポーネントを渡す場合@Component({entryComponents: [comp1, comp2], ...})-使用できます.resolveComponentFactory(componentToRender);
  • 実際のランタイム コンパイル、Compiler...

しかし、問題はそれをどのように使用するCompilerかです。上記のメモには、次のように電話する必要があると書かれていCompiler.compileComponentSync/Asyncます。

例えば。ある種の設定に対して、この種のテンプレートを(いくつかの構成条件に基づいて)作成したい

<form>
   <string-editor
     [propertyName]="'code'"
     [entity]="entity"
   ></string-editor>
   <string-editor
     [propertyName]="'description'"
     [entity]="entity"
   ></string-editor>
   ...

別のケースでは、これ(string-editorは に置き換えられtext-editorます)

<form>
   <text-editor
     [propertyName]="'code'"
     [entity]="entity"
   ></text-editor>
   ...

など(プロパティの種類ごとに異なる番号/日付/参照editors、一部のユーザーの一部のプロパティをスキップ...)。つまり、これは一例であり、実際の構成では、はるかに異なる複雑なテンプレートが生成される可能性があります。

テンプレートが変更されているため、ComponentFactoryResolver既存のものを使用して渡すことができません... Compiler.

4

16 に答える 16

170

編集 - 2.3.0に関連(2016-12-07)

注: 以前のバージョンの解決策を得るには、この投稿の履歴を確認してください

同様のトピックがここで議論されています Angular 2 の $compile に相当します。と を使用する必要がJitCompilerありNgModuleます。NgModuleAngular2の詳細については、こちらをご覧ください。

手短に

動作中の plunker/example があります(動的テンプレート、動的コンポーネント タイプ、動的モジュールなどJitCompiler)。

プリンシパルは次のとおりです
。1)テンプレートの作成
2)ComponentFactoryキャッシュ内の検索- 7)に移動
3) - Component
4) の作成 - Module
5) の作成 - コンパイルModule
6) - 戻り (および後で使用するためにキャッシュ) ComponentFactory
7)ターゲットの使用およびComponentFactoryインスタンスの作成動的のComponent

ここにコード スニペットがあります(詳細はこちら) - カスタム ビルダーは、ビルド/キャッシュされたばかりを返しComponentFactory、ビュー ターゲット プレースホルダーは、のインスタンスを作成するために消費します。DynamicComponent

  // here we get a TEMPLATE with dynamic content === TODO
  var template = this.templateBuilder.prepareTemplate(this.entity, useTextarea);

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

これはそれです - 一言で言えばそれです。詳細を取得するには..以下をお読みください

.

TL&DR

プランカーを観察し、一部のスニペットでさらに説明が必要な場合に備えて、戻って詳細を読みます

.

詳細説明 - Angular2 RC6++ &ランタイム コンポーネント

このシナリオの説明の下で、

  1. モジュールPartsModule:NgModule (小片のホルダー)を作成します
  2. DynamicModule:NgModule動的コンポーネントを含む別のモジュールを作成します(およびPartsModule動的に参照します)
  3. 動的テンプレートを作成する(単純なアプローチ)
  4. 新しいComponentタイプを作成する(テンプレートが変更された場合のみ)
  5. 新しいを作成しますRuntimeModule:NgModule。このモジュールには、以前に作成したComponentタイプが含まれます
  6. 電話JitCompiler.compileModuleAndAllComponentsAsync(runtimeModule)してもらうComponentFactory
  7. DynamicComponentビュー ターゲット プレースホルダーの - ジョブのインスタンスを作成し、ComponentFactory
  8. 新しいインスタンスに割り当てる(@Inputsから編集に切り替える) 消費する INPUTTEXTAREA@Outputs

Ngモジュール

sが必要NgModuleです。

非常に単純な例を示したいと思いますが、この場合、3 つのモジュールが必要になります(実際には 4 つですが、AppModule は数えません)。本当に堅実な動的コンポーネント ジェネレーターの基礎として、単純なスニペットではなく、これを使用してください。

( ...)など、すべての小さなコンポーネントに対して1 つのモジュールがあります。string-editortext-editor date-editornumber-editor

@NgModule({
  imports:      [ 
      CommonModule,
      FormsModule
  ],
  declarations: [
      DYNAMIC_DIRECTIVES
  ],
  exports: [
      DYNAMIC_DIRECTIVES,
      CommonModule,
      FormsModule
  ]
})
export class PartsModule { }

拡張DYNAMIC_DIRECTIVES可能で、動的コンポーネント テンプレート/タイプに使用されるすべての小さなパーツを保持するためのものです。app/parts/parts.module.ts を確認してください

2 つ目は、動的スタッフ処理のモジュールです。これには、ホスティング コンポーネントといくつかのプロバイダーが含まれます。シングルトンになります。そのため、標準的な方法で公開します-forRoot()

import { DynamicDetail }          from './detail.view';
import { DynamicTypeBuilder }     from './type.builder';
import { DynamicTemplateBuilder } from './template.builder';

@NgModule({
  imports:      [ PartsModule ],
  declarations: [ DynamicDetail ],
  exports:      [ DynamicDetail],
})

export class DynamicModule {

    static forRoot()
    {
        return {
            ngModule: DynamicModule,
            providers: [ // singletons accross the whole app
              DynamicTemplateBuilder,
              DynamicTypeBuilder
            ], 
        };
    }
}

の使用方法を確認してforRoot()くださいAppModule

DynamicTypeBuilder最後に、アドホックなランタイム モジュールが必要になりますが、これはジョブの一部として後で作成されます。

4 番目のモジュールであるアプリケーション モジュールは、宣言コンパイラ プロバイダーを保持するモジュールです。

...
import { COMPILER_PROVIDERS } from '@angular/compiler';    
import { AppComponent }   from './app.component';
import { DynamicModule }    from './dynamic/dynamic.module';

@NgModule({
  imports:      [ 
    BrowserModule,
    DynamicModule.forRoot() // singletons
  ],
  declarations: [ AppComponent],
  providers: [
    COMPILER_PROVIDERS // this is an app singleton declaration
  ],

そこでNgModuleについてもっと読んでください(読んでください)

テンプレートビルダー_

この例では、この種のエンティティの詳細を処理します

entity = { 
    code: "ABC123",
    description: "A description of this Entity" 
};

を作成するtemplateには、このplunkerでこのシンプル/ナイーブ ビルダーを使用します。

真のソリューションである真のテンプレート ビルダーは、アプリケーションが多くのことを実行できる場所です。

// plunker - app/dynamic/template.builder.ts
import {Injectable} from "@angular/core";

@Injectable()
export class DynamicTemplateBuilder {

    public prepareTemplate(entity: any, useTextarea: boolean){
      
      let properties = Object.keys(entity);
      let template = "<form >";
      let editorName = useTextarea 
        ? "text-editor"
        : "string-editor";
        
      properties.forEach((propertyName) =>{
        template += `
          <${editorName}
              [propertyName]="'${propertyName}'"
              [entity]="entity"
          ></${editorName}>`;
      });
  
      return template + "</form>";
    }
}

ここでのトリックは、既知のプロパティのセットを使用するテンプレートを作成することentityです。このようなプロパティ (-ies) は、次に作成する動的コンポーネントの一部である必要があります。

もう少し簡単にするために、インターフェイスを使用して、テンプレート ビルダーが使用できるプロパティを定義できます。これは、動的コンポーネント タイプによって実装されます。

export interface IHaveDynamicData { 
    public entity: any;
    ...
}

ComponentFactoryビルダー_

ここで非常に重要なことは、次のことに注意してください。

コンポーネント タイプである build with ourは異なる可能性がありますが、そのテンプレート(上記で作成)DynamicTypeBuilderのみが異なります。コンポーネントのプロパティ(入力、出力、または一部の保護) は同じです。別のプロパティが必要な場合は、テンプレートとタイプ ビルダーの別の組み合わせを定義する必要があります。

したがって、私たちはソリューションの核心に触れています。Builder は、1) 作成ComponentType2) 作成NgModule3) コンパイルComponentFactory4)後で再利用するためにキャッシュします。

受け取る必要がある依存関係:

// plunker - app/dynamic/type.builder.ts
import { JitCompiler } from '@angular/compiler';
    
@Injectable()
export class DynamicTypeBuilder {

  // wee need Dynamic component builder
  constructor(
    protected compiler: JitCompiler
  ) {}

そして、ここに取得する方法のスニペットがありますComponentFactory:

// plunker - app/dynamic/type.builder.ts
// this object is singleton - so we can use this as a cache
private _cacheOfFactories:
     {[templateKey: string]: ComponentFactory<IHaveDynamicData>} = {};
  
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);
            });
    });
}

上記では、 との両方を作成してキャッシュします。テンプレート(実際には、そのすべての実際の動的部分)が同じである場合..再利用できるためです。ComponentModule

そして、実行時に装飾されたクラス/型を作成するための本当にクールな方法を表す 2 つのメソッドがあります。だけでなく@Component@NgModule

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

重要:

コンポーネントの動的タイプは異なりますが、テンプレートが異なるだけです。そのため、その事実を使用してそれらをキャッシュします。これは本当にとても重要です。Angular2 はこれらもキャッシュします.. typeによって。同じテンプレート文字列の新しい型を再作成すると、メモリ リークが発生し始めます。

ComponentFactoryホスティング コンポーネントによって使用される

最後のピースは、動的コンポーネントのターゲットをホストするコンポーネント<div #dynamicContentPlaceHolder></div>です。それへの参照を取得し、それを使用ComponentFactoryしてコンポーネントを作成します。それは一言で言えば、ここにそのコンポーネントのすべての部分があります(必要に応じて、ここでプランカーを開きます

最初に import ステートメントを要約しましょう。

import {Component, ComponentRef,ViewChild,ViewContainerRef}   from '@angular/core';
import {AfterViewInit,OnInit,OnDestroy,OnChanges,SimpleChange} from '@angular/core';

import { IHaveDynamicData, DynamicTypeBuilder } from './type.builder';
import { DynamicTemplateBuilder }               from './template.builder';

@Component({
  selector: 'dynamic-detail',
  template: `
<div>
  check/uncheck to use INPUT vs TEXTAREA:
  <input type="checkbox" #val (click)="refreshContent(val.checked)" /><hr />
  <div #dynamicContentPlaceHolder></div>  <hr />
  entity: <pre>{{entity | json}}</pre>
</div>
`,
})
export class DynamicDetail implements AfterViewInit, OnChanges, OnDestroy, OnInit
{ 
    // wee need Dynamic component builder
    constructor(
        protected typeBuilder: DynamicTypeBuilder,
        protected templateBuilder: DynamicTemplateBuilder
    ) {}
    ...

テンプレートとコンポーネントビルダーを受け取るだけです。次は、この例に必要なプロパティです(詳細はコメントで)

// reference for a <div> with #dynamicContentPlaceHolder
@ViewChild('dynamicContentPlaceHolder', {read: ViewContainerRef}) 
protected dynamicComponentTarget: ViewContainerRef;
// this will be reference to dynamic content - to be able to destroy it
protected componentRef: ComponentRef<IHaveDynamicData>;

// until ngAfterViewInit, we cannot start (firstly) to process dynamic stuff
protected wasViewInitialized = false;

// example entity ... to be recieved from other app parts
// this is kind of candiate for @Input
protected entity = { 
    code: "ABC123",
    description: "A description of this Entity" 
  };

この単純なシナリオでは、ホスティング コンポーネントには@Input. したがって、変更に対応する必要はありません。しかし、その事実にもかかわらず(そして今後の変更に備えるために) 、コンポーネントがすでに(最初に)開始されている場合は、フラグを導入する必要があります。そうして初めて、魔法を始めることができます。

最後に、コンポーネント ビルダーを使用し、そのコンパイル済み/キャッシュ済みの ComponentFacotry. Target プレースホルダーは、そのファクトリでをインスタンス化するComponentよう求められます。

protected refreshContent(useTextarea: boolean = false){
  
  if (this.componentRef) {
      this.componentRef.destroy();
  }
  
  // here we get a TEMPLATE with dynamic content === TODO
  var template = this.templateBuilder.prepareTemplate(this.entity, useTextarea);

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

小さな延長

destroy()また、コンパイルされたテンプレートへの参照を保持する必要があります..それを変更するたびに適切にできるようにする必要があります。

// this is the best moment where to start to process dynamic stuff
public ngAfterViewInit(): void
{
    this.wasViewInitialized = true;
    this.refreshContent();
}
// wasViewInitialized is an IMPORTANT switch 
// when this component would have its own changing @Input()
// - then we have to wait till view is intialized - first OnChange is too soon
public ngOnChanges(changes: {[key: string]: SimpleChange}): void
{
    if (this.wasViewInitialized) {
        return;
    }
    this.refreshContent();
}

public ngOnDestroy(){
  if (this.componentRef) {
      this.componentRef.destroy();
      this.componentRef = null;
  }
}

終わり

それはほとんどそれです。動的に構築されたものを破棄することを忘れないでください(ngOnDestroy)。また、動的にキャッシュし、唯一の違いがテンプレートである場合は必ずキャッシュしてください。typesmodules

ここですべての動作を確認してください

この投稿の以前のバージョン(RC5 関連など)を確認するには、履歴を確認してください

于 2016-08-11T05:37:07.113 に答える
59

編集 (26/08/2017) : 以下のソリューションは、Angular2 および 4 でうまく機能します。テンプレート変数とクリック ハンドラーを含むように更新し、Angular 4.3 でテストしました。
Angular4 の場合、Ophir の回答で説明されている ngComponentOutletがはるかに優れたソリューションです。ただし、現在はまだ入力と出力をサポートしていません。[この PR]( https://github.com/angular/angular/pull/15362]が受け入れられる場合、create イベントによって返されるコンポーネント インスタンスを通じて可能になります
。ng-dynamic-componentが最適で最も単純な場合があります。完全に解決策ですが、まだテストしていません。

@Long Fieldの答えは的を射ています!別の (同期) 例を次に示します。

import {Compiler, Component, NgModule, OnInit, ViewChild,
  ViewContainerRef} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'

@Component({
  selector: 'my-app',
  template: `<h1>Dynamic template:</h1>
             <div #container></div>`
})
export class App implements OnInit {
  @ViewChild('container', { read: ViewContainerRef }) container: ViewContainerRef;

  constructor(private compiler: Compiler) {}

  ngOnInit() {
    this.addComponent(
      `<h4 (click)="increaseCounter()">
        Click to increase: {{counter}}
      `enter code here` </h4>`,
      {
        counter: 1,
        increaseCounter: function () {
          this.counter++;
        }
      }
    );
  }

  private addComponent(template: string, properties?: any = {}) {
    @Component({template})
    class TemplateComponent {}

    @NgModule({declarations: [TemplateComponent]})
    class TemplateModule {}

    const mod = this.compiler.compileModuleAndAllComponentsSync(TemplateModule);
    const factory = mod.componentFactories.find((comp) =>
      comp.componentType === TemplateComponent
    );
    const component = this.container.createComponent(factory);
    Object.assign(component.instance, properties);
    // If properties are changed at a later stage, the change detection
    // may need to be triggered manually:
    // component.changeDetectorRef.detectChanges();
  }
}

@NgModule({
  imports: [ BrowserModule ],
  declarations: [ App ],
  bootstrap: [ App ]
})
export class AppModule {}

http://plnkr.co/edit/fdP9Ocに住んでいます。

于 2016-09-15T09:41:20.627 に答える
18

学んだことをすべて 1 つのファイルに圧縮することにしました。特にRC5以前と比べて、ここで取り入れるべきことがたくさんあります。このソース ファイルには、AppModule と AppComponent が含まれていることに注意してください。

import {
  Component, Input, ReflectiveInjector, ViewContainerRef, Compiler, NgModule, ModuleWithComponentFactories,
  OnInit, ViewChild
} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';

@Component({
  selector: 'app-dynamic',
  template: '<h4>Dynamic Components</h4><br>'
})
export class DynamicComponentRenderer implements OnInit {

  factory: ModuleWithComponentFactories<DynamicModule>;

  constructor(private vcRef: ViewContainerRef, private compiler: Compiler) { }

  ngOnInit() {
    if (!this.factory) {
      const dynamicComponents = {
        sayName1: {comp: SayNameComponent, inputs: {name: 'Andrew Wiles'}},
        sayAge1: {comp: SayAgeComponent, inputs: {age: 30}},
        sayName2: {comp: SayNameComponent, inputs: {name: 'Richard Taylor'}},
        sayAge2: {comp: SayAgeComponent, inputs: {age: 25}}};
      this.compiler.compileModuleAndAllComponentsAsync(DynamicModule)
        .then((moduleWithComponentFactories: ModuleWithComponentFactories<DynamicModule>) => {
          this.factory = moduleWithComponentFactories;
          Object.keys(dynamicComponents).forEach(k => {
            this.add(dynamicComponents[k]);
          })
        });
    }
  }

  addNewName(value: string) {
    this.add({comp: SayNameComponent, inputs: {name: value}})
  }

  addNewAge(value: number) {
    this.add({comp: SayAgeComponent, inputs: {age: value}})
  }

  add(comp: any) {
    const compFactory = this.factory.componentFactories.find(x => x.componentType === comp.comp);
    // If we don't want to hold a reference to the component type, we can also say: const compFactory = this.factory.componentFactories.find(x => x.selector === 'my-component-selector');
    const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector);
    const cmpRef = this.vcRef.createComponent(compFactory, this.vcRef.length, injector, []);
    Object.keys(comp.inputs).forEach(i => cmpRef.instance[i] = comp.inputs[i]);
  }
}

@Component({
  selector: 'app-age',
  template: '<div>My age is {{age}}!</div>'
})
class SayAgeComponent {
  @Input() public age: number;
};

@Component({
  selector: 'app-name',
  template: '<div>My name is {{name}}!</div>'
})
class SayNameComponent {
  @Input() public name: string;
};

@NgModule({
  imports: [BrowserModule],
  declarations: [SayAgeComponent, SayNameComponent]
})
class DynamicModule {}

@Component({
  selector: 'app-root',
  template: `
        <h3>{{message}}</h3>
        <app-dynamic #ad></app-dynamic>
        <br>
        <input #name type="text" placeholder="name">
        <button (click)="ad.addNewName(name.value)">Add Name</button>
        <br>
        <input #age type="number" placeholder="age">
        <button (click)="ad.addNewAge(age.value)">Add Age</button>
    `,
})
export class AppComponent {
  message = 'this is app component';
  @ViewChild(DynamicComponentRenderer) dcr;

}

@NgModule({
  imports: [BrowserModule],
  declarations: [AppComponent, DynamicComponentRenderer],
  bootstrap: [AppComponent]
})
export class AppModule {}`
于 2016-09-29T14:39:16.603 に答える
4

Radim によるこの非常に優れた投稿に加えて、いくつかの詳細を追加したいと思います。

私はこの解決策を取り、少しの間それに取り組み、すぐにいくつかの制限に遭遇しました。それらの概要を説明し、それに対する解決策も示します。

  • まず第一に、dynamic-detail 内に dynamic-detail をレンダリングできませんでした (基本的に、動的 UI を互いに入れ子にします)。
  • 次の問題は、ソリューションで利用可能になったパーツの 1 つに動的な詳細をレンダリングしたいということでした。それも最初のソリューションでは不可能でした。
  • 最後に、string-editor のような動的パーツでテンプレート URL を使用することはできませんでした。

この投稿に基づいて、これらの制限を達成する方法について別の質問をしました。これは次の場所にあります。

angular2での再帰的な動的テンプレートのコンパイル

私と同じ問題に遭遇した場合に備えて、これらの制限に対する回答の概要を説明します。これにより、ソリューションがより柔軟になります。初期のプランカーもそれに合わせて更新されると最高です。

dynamic-detail を相互にネストできるようにするには、type.builder.tsの import ステートメントに DynamicModule.forRoot() を追加する必要があります。

protected createComponentModule (componentType: any) {
    @NgModule({
    imports: [
        PartsModule, 
        DynamicModule.forRoot() //this line here
    ],
    declarations: [
        componentType
    ],
    })
    class RuntimeComponentModule
    {
    }
    // a module for just this Type
    return RuntimeComponentModule;
}

<dynamic-detail>その上、 string-editor または text-editor のいずれかのパーツ内で使用することはできませんでした。

それを有効にするには、変更する必要がありparts.module.tsますdynamic.module.ts

内部に追加するparts.module.ts必要がありますDynamicDetailDYNAMIC_DIRECTIVES

export const DYNAMIC_DIRECTIVES = [
   forwardRef(() => StringEditor),
   forwardRef(() => TextEditor),
   DynamicDetail
];

またdynamic.module.ts、dynamicDetail はパーツの一部になっているため、削除する必要があります。

@NgModule({
   imports:      [ PartsModule ],
   exports:      [ PartsModule],
})

動作する修正されたプランカーはここにあります: http://plnkr.co/edit/UYnQHF?p=preview (私はこの問題を解決していません。私はただのメッセンジャーです:-D)

最後に、動的コンポーネントで作成されたパーツで templateurl を使用できませんでした。解決策(または回避策。角度のあるバグなのか、フレームワークの間違った使用なのかはわかりません)は、コンパイラを注入するのではなく、コンストラクタで作成することでした。

    private _compiler;

    constructor(protected compiler: RuntimeCompiler) {
        const compilerFactory : CompilerFactory =
        platformBrowserDynamic().injector.get(CompilerFactory);
        this._compiler = compilerFactory.createCompiler([]);
    }

次に、を使用し_compilerてコンパイルすると、templateUrls も有効になります。

return new Promise((resolve) => {
        this._compiler
            .compileModuleAndAllComponentsAsync(module)
            .then((moduleWithFactories) =>
            {
                let _ = window["_"];
                factory = _.find(moduleWithFactories.componentFactories, { componentType: type });

                this._cacheOfFactories[template] = factory;

                resolve(factory);
            });
    });

これが他の誰かに役立つことを願っています!

よろしくモーテン

于 2016-10-05T11:26:16.413 に答える
3

これは、サーバーから生成された動的フォーム コントロールの例です。

https://stackblitz.com/edit/angular-t3mmg6

この例は動的なフォーム コントロールであり、コンポーネントを追加します (ここで、サーバーからフォーム コントロールを取得できます)。addcomponent メソッドが表示される場合は、フォーム コントロールが表示されます。この例では、角度のあるマテリアルを使用していませんが、機能します (@ work を使用しています)。これは angular 6 を対象としていますが、以前のすべてのバージョンで機能します。

AngularVersion 5 以降では JITComplierFactory を追加する必要があります。

ありがとう

ビジェイ

于 2018-10-05T12:24:01.160 に答える
2

私自身、RC4 を RC5 に更新する方法を調べようとしていますが、このエントリに出くわしました。動的コンポーネント作成への新しいアプローチにはまだ少し謎が残っているため、コンポーネント ファクトリ リゾルバについては何も提案しません。

しかし、私が提案できるのは、このシナリオでコンポーネントを作成するためのもう少し明確なアプローチです。次のように、条件に応じて文字列エディターまたはテキストエディターを作成するテンプレートでスイッチを使用するだけです:

<form [ngSwitch]="useTextarea">
    <string-editor *ngSwitchCase="false" propertyName="'code'" 
                 [entity]="entity"></string-editor>
    <text-editor *ngSwitchCase="true" propertyName="'code'" 
                 [entity]="entity"></text-editor>
</form>

ところで、[prop] 式の "[" には意味があります。これは一方向のデータ バインディングを示します。したがって、プロパティを変数にバインドする必要がないことがわかっている場合は、それらを省略してもかまいません。

于 2016-08-11T07:31:45.350 に答える
0

この特定のケースでは、ディレクティブを使用してコンポーネントを動的に作成する方が適切なオプションのようです。例:

コンポーネントを作成する HTML 内

<ng-container dynamicComponentDirective [someConfig]="someConfig"></ng-container>

私は、次の方法でディレクティブにアプローチし、設計します。

const components: {[type: string]: Type<YourConfig>} = {
    text : TextEditorComponent,
    numeric: NumericComponent,
    string: StringEditorComponent,
    date: DateComponent,
    ........
    .........
};

@Directive({
    selector: '[dynamicComponentDirective]'
})
export class DynamicComponentDirective implements YourConfig, OnChanges, OnInit {
    @Input() yourConfig: Define your config here //;
    component: ComponentRef<YourConfig>;

    constructor(
        private resolver: ComponentFactoryResolver,
        private container: ViewContainerRef
    ) {}

    ngOnChanges() {
        if (this.component) {
            this.component.instance.config = this.config;
            // config is your config, what evermeta data you want to pass to the component created.
        }
    }

    ngOnInit() {
        if (!components[this.config.type]) {
            const supportedTypes = Object.keys(components).join(', ');
            console.error(`Trying to use an unsupported type ${this.config.type} Supported types: ${supportedTypes}`);
        }

        const component = this.resolver.resolveComponentFactory<yourConfig>(components[this.config.type]);
        this.component = this.container.createComponent(component);
        this.component.instance.config = this.config;
    }
}

したがって、コンポーネントのテキスト、文字列、日付など、要素内の HTML で渡した構成は何でもng-container利用できます。

configyourConfigは同じで、メタデータを定義できます。

構成または入力タイプに応じて、ディレクティブはそれに応じて動作する必要があり、サポートされているタイプから、適切なコンポーネントをレンダリングします。そうでない場合、エラーがログに記録されます。

于 2019-05-20T19:23:00.023 に答える