动态添加组件的Angular 5胶合逻辑以形成

时间:2018-02-02 00:23:57

标签: angular angular5

我正在使用 Angular 5 ,我需要创建一个组件(dynform)来实例化一组自定义组件(dyncompA,{{1等等)。哪些是dyncompB决定的,因此它们不会在父模板中声明,而是动态添加。每个子组件都包含从dynform.ngOnInit(非字符串)派生的MyObjectAMyObjectB等类型的值,我为其实现了MyObjectAbstract接口。

我得到的问题是,永远不会通知父表单有关子组件的有效性状态或其更改的(!pristine)状态。我的自定义验证器也没有被调用过。此外,子组件未从ControlValueAccessor接收其属性值。我可以看到AbstractControl' s ComponentA从未被调用,没有人订阅该组件的registerOnChange事件。但是,如果我在模板中静态使用valueChange @Output,则所有这些都有效:调用验证器,正确传播更改等等。我真的不知道我的问题是来自{{1} },ComponentA或两者。

对于我使用此模板开始的dynform

componentA

我的dynform代码有:

<form (ngSubmit)="test()" [formGroup]="fgroup">
    <div #container></div>
</form>

嗯,无论如何,那就是这个概念。典型的dynform就像:

@Component({
    selector: 'dynform',
    templateUrl: '<form (ngSubmit)="test()" [formGroup]="fgroup"><div #container></div></form>'
    ]
})
export class DynForm implements OnInit, OnDestroy {

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

    private mappings: any = {
        'compA': { type: ComponentA },
        'compB': { type: ComponentB },
        ...
    }

    @Input valuecollection: MyObjectAbstract[];    // Set by instantiator

    fgroup: FormGroup;

    private getComponentFactory(value: compValue): ComponentFactory<{}> {
        let entry = this.mappings[value.getCompType()];
        return this.resolver.resolveComponentFactory(entry.type);
    }

    static myValidation(control: AbstractControl): ValidationErrors | null {
        let err = {
            myValidationError: {
                given: control.value,
                max: 10,
                min: 0
            }
        }
        // Never called anyway
        return control.value.toString() == "error" ? err : null;
    }

    ngOnInit() {
        this.valuecollection.( value => {
            let name = value.name;
            let ref = this.container.createComponent(
                this.getComponentFactory(value)
            );
            ref.instance.value = value;
            ref.instance.name = name;   // IS THIS OK?
            let control = new FormControl(value, [DynForm.myValidation]);
            this.fgroup.addControl(name, control);
        });
    }

    ngOnDestroy() {
        // Call the created references' destroy() method
    }
}

我已经在StackOverflow中读到ComponentA并不真正适用于动态加载的组件;这也是我实施v @Component({ selector: 'component-a', templateUrl: '<stuff></stuff>', providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: ComponentA, multi: true }, ] }) export class ComponentA implements OnInit, DoCheck, ControlValueAccessor { @Input() value: MyObjectAbstract; @Input('formControlName') fname: string; // What? @Output() valueChange = new EventEmitter<MyObjectAbstract>(); private propagateChange : (_: any) => {}; private _prevValue: string; getFieldInstanceChange(): EventEmitter<FieldInstance> { return this.fieldInstanceChange; } ngOnInit() { // TODO: Connect inputFieldText in the view with the field instance (onblur?) // console.log(`BizbookStringInputComponent()[${this.name}].ngOnInit()`); if (this.fieldInstance && this.fieldInstance instanceof FieldInstance) { this.inputFieldName = this.fieldInstance.base.description; this.inputFieldText = (this.fieldInstance.value as string); } else { // this.inputFieldName = this.name; this.inputFieldText = '(no field instance)'; } } ngDoCheck() { if (this._prevValue == this.value.toString()) return; if (this.propagateChange) { // Never gets in here if added dynamically this._prevValue = value.toString(); this.propagateChange(this.fieldInstance); } else { // Always gets in here if added dynamically console.log(`propagateChange()[${this.name}].ngDoCheck(): change!: "${this.value.toString()}", but propagateChange not yet set.`); this._prevValue = this.value.toString(); } } writeValue(value: any) { if (value instanceof MyObjectAbstract && value !== this.value) { this.value = (value as MyObjectAbstract); } } registerOnChange(fn: any) { // Never called if instantiated dynamically this.propagateChange = fn; } registerOnTouched(fn: any) { // Not used } } 的原因。但问题似乎来自于ngForm验证逻辑与ControlValueAccessor指令相关联的事实,我不知道在创建动态控件之前如何应用/生成它。

我已经关注this thread,但我无法让它发挥作用。实际上我很难理解一些概念,因为我是 Angular 的新手。

我一直试图让这项工作好几天,并阅读了很多关于验证器,自定义验证器,自定义组件,动态组件等的文章,但无济于事。我非常感谢你的帮助。

1 个答案:

答案 0 :(得分:0)

看起来我的整个方法都是不必要的错综复杂的。在这篇文章中解释了解决这个问题的正确方法,其中还包括源代码:

https://toddmotto.com/angular-dynamic-components-forms

基本上,您需要做的是在<ng-container YourAttribute [formControlName]="something">上使用ngFor循环。 YourAttribute是一个动态创建组件的指令。注意[formControlName]的[]语法,因为它会将值(和FormControlName指令!)注入YourAttribute。

链接的项目工作得很漂亮,但我在我的指令中添加了ControlValueAccessor(因为我没有使用DefaultValueAccessor)。然后我的指令需要通过setTimeout将ControlValueAccessor方法链接到实例化控件中以避免“Error: Expression has changed after it was checked. Inside of nested ControlValueAccessor”。