2016-10-24 1 views
2

Я создал некоторые пользовательские компоненты, каждый из которых реализует ControlValueAccessor, чтобы они могли функционировать как элементы управления в пределах FormGroup. Обычно остается только добавить директиву formControlName для каждого настраиваемого компонента в HTML.Как зарегистрировать динамически добавленный пользовательский компонент в качестве элемента управления формой в группе форм

Однако я добавляю эти компоненты в форму во время выполнения с использованием метода this.

Моя проблема заключается в том, что я не могу зарегистрировать эти компоненты с содержанием FormGroup, потому что я не могу объявить директиву formControlName (или любую директиву, если на то пошло) с динамически добавленным элементом управления.

Кто-нибудь еще обнаружил, как это возможно? На данный момент я обертываю каждый элемент управления внутри другого компонента, чтобы использовать директиву formControlName, но это слишком уродливо и трудоемко.


Ниже разобранный пример того, что я уже реализован как автономное приложение Angular2, которая показывает компонент (CustomComponent) быть программно добавлены при запуске. Чтобы связать CustomComponent с FormGroup, мне пришлось создать 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

no code no cry ... Добавить, что вы пробовали здесь – smnbbrv

+1

Вы совершенно правы. Я добавлю код –

+0

спасибо за обновление. Каково конечное использование ваших динамически созданных компонентов? Вы хотите иметь динамическую форму только с динамическими элементами управления или вы хотите иметь возможность использовать элементы управления один за другим? – smnbbrv

ответ

2

Если я не неправильно понять ваши потребности ...

Weird вещь, что вы приложите форму группы внутри 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

Это действительно работает очень хорошо. Большое спасибо за ваше время. –