2016-10-24 35 views
2

我已經創建了一些定製組件,其中的每一個實現ControlValueAccessor,以便它們可以作爲FormGroup內控制功能內註冊一個動態添加定製組件的一種形式的控制。通常,唯一要做的事情就是將formControlName指令添加到HTML中的每個自定義組件。如何將formgroup

不過,我在運行時使用this技術附加這些部件的形式。

我的問題是,我不能註冊這些組件與包含FormGroup因爲我不能聲明formControlName指令(或任何指令,對於這一點)與動態添加控制。

有其他人發現瞭如何這可能是可能的嗎?目前,我正在包裝的另一個組件內的每個控制,這樣我就可以使用formControlName指令,但是這僅僅是太醜陋和勞動密集型的。


下面是什麼,我已經作爲一個獨立的應用程序Angular2,這說明了一個組件(CustomComponent)實現在啓動時被編程添加一個精簡的例子。爲了綁定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

感謝您的更新。動態創建組件的最終用途是什麼?你想只有動態控件的動態表單,或者你想有一個一個地使用控件的能力嗎? – smnbbrv

回答

2

如果我沒有誤解你的需求...

奇怪的是,您將您的表單組的AppComponent內。相反,您可以動態創建完整的表單。爲了做到這一點,你需要實現兩個功能:

  1. 一個產生從JSON配置模板
  2. 另外一個產生從JSON配置形式

然後,只需建立一個大單包含所有輸入的容器組件。我會建議在這裏使用服務:

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

} 

這當然只是一個簡化版本,但它工作正常,我不是隻對純形式,但對嵌套表格組/陣列/控制項目。

一旦注入服務,您可以使用這兩個函數生成模塊和組件。

+1

這實際上工作得很好。非常感謝您的時間。 –