2016-10-24 8 views
2

FormGroup内のコントロールとして機能するために、それぞれがControlValueAccessorを実装するカスタムコンポーネントをいくつか作成しました。通常は、HTMLの各カスタムコンポーネントにformControlNameディレクティブを追加するだけです。フォームグループ内のフォームコントロールとして動的に追加されたカスタムコンポーネントを登録する方法

しかし、実行時にこれらのコンポーネントをthisテクニックを使用してフォームに追加しています。

私の問題は、FormGroupを含むこれらのコンポーネントを登録することができないということです。動的に追加されたコントロールでformControlNameディレクティブ(または任意のディレクティブ)を宣言できないためです。

これがどのように可能であるか他の誰かが発見しましたか?現時点では、formControlNameディレクティブを使用できるように、各コントロールを別のコンポーネントにラップしていますが、これはあまりにも面倒で労力がかかります。以下


Iはすでにプログラムで起動時に添加される成分(CustomComponent)を示すスタンドアロンAngular2アプリとして実装した内容のダウンストリッピング一例です。 CustomComponentFormGroupにバインドするには、避けたければCustomContainerComponentを作成しなければなりませんでした。

import { 
    Component, ReflectiveInjector, ViewContainerRef, Compiler, NgModule, ModuleWithComponentFactories, 
    OnInit, ViewChild, forwardRef 
} from '@angular/core'; 
import {BrowserModule} from '@angular/platform-browser'; 
import {NG_VALUE_ACCESSOR, ControlValueAccessor, FormGroup, FormBuilder, ReactiveFormsModule} from '@angular/forms'; 


export class AbstractValueAccessor<T> implements ControlValueAccessor { 
    _value: T; 
    get value(): T { 
    return this._value; 
    }; 

    set value(v: T) { 
    if (v !== this._value) { 
     this._value = v; 
     this.onChange(v); 
    } 
    } 

    writeValue(value: T) { 
    this._value = value; 
    this.onChange(value); 
    } 

    onChange = (_) => {}; 
    onTouched =() => {}; 

    registerOnChange(fn: (_: any) => void): void { 
    this.onChange = fn; 
    } 

    registerOnTouched(fn:() => void): void { 
    this.onTouched = fn; 
    } 

} 

export function MakeProvider(type: any) { 
    return { 
    provide: NG_VALUE_ACCESSOR, 
    useExisting: forwardRef(() => type), 
    multi: true 
    }; 
} 

@Component({ 
    selector: 'app-custom', 
    template: `<input type="text" [value]="value">`, 
    providers: [MakeProvider(CustomComponent)] 
}) 
class CustomComponent extends AbstractValueAccessor<string> { 

} 

@Component({ 
    selector: 'app-custom-container', 
    template: `<div [formGroup]="formGroup"><app-custom formControlName="comp"></app-custom></div>` 
}) 
class CustomContainerComponent { 
    formGroup: FormGroup; 
} 

@NgModule({ 
    imports: [BrowserModule, ReactiveFormsModule], 
    declarations: [CustomComponent, CustomContainerComponent] 
}) 
class DynamicModule { 
} 

@Component({ 
    selector: 'app-root', 
    template: `<h4>Dynamic Components</h4><br> 
      <form [formGroup]="formGroup"> 
       <div #dynamicContentPlaceholder></div> 
      </form>` 
}) 
export class AppComponent implements OnInit { 

    @ViewChild('dynamicContentPlaceholder', {read: ViewContainerRef}) 
    public readonly vcRef: ViewContainerRef; 

    factory: ModuleWithComponentFactories<DynamicModule>; 
    formGroup: FormGroup; 

    constructor(private compiler: Compiler, private formBuilder: FormBuilder) { 
    } 

    ngOnInit() { 
    this.compiler.compileModuleAndAllComponentsAsync(DynamicModule) 
     .then((moduleWithComponentFactories: ModuleWithComponentFactories<DynamicModule>) => { 
     this.factory = moduleWithComponentFactories; 
     const compFactory = this.factory.componentFactories.find(x => x.selector === 'app-custom-container'); 
     const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector); 
     let cmp = this.vcRef.createComponent(compFactory, this.vcRef.length, injector, []); 
     (<CustomContainerComponent>cmp.instance).formGroup = this.formGroup; 
     }); 

    this.formGroup = this.formBuilder.group({ 
     'comp': ['hello world'] 
    }) 
    } 
} 

@NgModule({ 
    imports: [BrowserModule, ReactiveFormsModule], 
    declarations: [AppComponent], 
    bootstrap: [AppComponent] 
}) 
export class AppModule { 
} 
+1

いいえコードはありません...あなたがここで試したものを追加してください。 – smnbbrv

+1

あなたはかなり正しいです。更新のためにいくつかのコード –

+0

を追加します。動的に作成されたコンポーネントの最終使用は何ですか?ダイナミックコントロールのみを使用してダイナミックフォームを作成したい場合や、コントロールを1つずつ使用する場合は、 – smnbbrv

答えて

2

私はあなたのニーズを誤解していない場合は...

奇妙なことは、あなたがAppComponentの内部でフォームグループを添付していることです。代わりに、完全なフォームを動的に作成することができます。あなたは2つの機能を実装する必要があることを行うために:

JSONの設定からテンプレートを生成
  1. 一つ

そして、単に大きなフォームを作成JSONの設定からフォームを生成し、もう1すべての入力を含むコンテナコンポーネント。私はここでサービスを使用することをお勧めします:

@Injectable() 
export class ModuleFactory { 

    public createComponent(jsonConfig: SomeInterface) { 
    @Component({ 
     template: ` 
     <div ngForm="form">${ /* and here generate the template containing all inputs */ }</div> 
     ` 
    }) 
    class FormContainerComponent { 
     public form: FormGroup = /* generate a form here */; 
    }; 

    return FormContainerComponent; 
    } 

    public createModule(component: any) { 
    @NgModule({ 
     imports: [ 
     CommonModule, 
     ReactiveFormsModule 
     ], 
     declarations: [ component ] 
    }) 
    class MyModule {}; 

    return MyModule; 
    } 

} 

はもちろん、これはただ単純化したバージョンですが、それはプレーンな形のためではなく、ネストされたフォームグループ/配列/コントロールのためだけではなく、私のプロジェクトのために正常に動作します。

サービスを挿入すると、これらの2つの機能を使用してモジュールとコンポーネントを生成できます。

+1

これは実際にはとてもうまく動作します。あなたのお時間をありがとうございました。 –

関連する問題