繁体   English   中英

动态添加到组件中的Angular 5胶合逻辑组件

[英]Angular 5 glue logic of components dynamically added to form

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

我得到的问题是父窗体从未通知子组件的有效性状态或它们的已更改(原始状态)。 我的自定义验证程序也从未被调用过。 此外,子组件不会从AbstractControl接收其属性值。 我可以看到从未调用ComponentAregisterOnChange并且没有人订阅该组件的valueChange @Output事件。 但是,如果我在模板中静态使用ComponentA ,则所有这些工作都有效:调用了验证器,正确地传播了更改等。我真的不知道我的问题是否来自dynformcomponentA或两者。

对于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
    }
}

好吧,无论如何,这就是概念。 典型的ComponentA如下所示:

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

我在StackOverflow上已经读到了一些内容,即ControlValueAccessor并不真正适用于动态加载的组件。 这就是为什么我还实现了alueChange @Output 但是问题似乎来自ngForm验证逻辑与@FormControlName指令绑定的@FormControlName ,我不知道如何在创建动态控件之前将其应用于动态控件。

我遵循了这个主题,但无法正常工作。 实际上,由于我是Angular的新手,所以我很难理解一些概念。

我一直在努力工作数天,并且阅读了很多关于验证器,自定义验证器,自定义组件,动态组件等的文章,但无济于事。 非常感谢您的帮助。

看来我的整个方法不必要地复杂。 在这篇文章中非常彻底地解释了解决此问题的正确方法,其中还包括源代码:

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

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

链接的项目工作得很漂亮,但是我将ControlValueAccessor添加到了指令中(因为我不使用DefaultValueAccessor)。 然后,我的指令需要通过setTimeout将ControlValueAccessor方法链接到实例化的控件中,以避免出现“ 错误:检查表达式后,表达式已更改。在嵌套的ControlValueAccessor内部 ”。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM