首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >角5胶合逻辑元件动态加入成型

角5胶合逻辑元件动态加入成型
EN

Stack Overflow用户
提问于 2018-02-02 00:23:57
回答 1查看 2.1K关注 0票数 1

我使用的是角5,我需要创建一个组件(dynform)来实例化一组定制组件(dyncompAdyncompB等)。哪些是在dynform.ngOnInit中决定的,所以它们没有在父模板中声明,而是动态添加。每个子组件都包含一个类型为MyObjectAMyObjectB等的值,该值来自于我实现ControlValueAccessor接口的MyObjectAbstract (而不是字符串)。

我遇到的问题是,父表单从来没有被通知过子组件的有效性状态,或者它们更改的(!原始的)状态。我的自定义验证器也没有被调用过。此外,子组件不会从AbstractControl接收它的属性值。我可以看到,ComponentAregisterOnChange从未被调用过,也没有人订阅组件的valueChange @Output事件。然而,如果我在模板中静态地使用ComponentA,所有这些都可以:验证器被调用,更改被正确传播等等。我不知道我的问题是来自dynformcomponentA,还是两者。

对于dynform,我从以下模板开始:

代码语言:javascript
运行
复制
<form (ngSubmit)="test()" [formGroup]="fgroup">
    <div #container></div>
</form>

我的dynform代码有:

代码语言:javascript
运行
复制
@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应该是这样的:

代码语言:javascript
运行
复制
@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并不真正适用于动态加载的组件;这就是为什么我也实现了valueChange @Output。但问题似乎来自这样一个事实,即ngForm验证逻辑与@FormControlName指令相关联,在创建它之前,我不知道如何将它应用/生成到动态控件。

我跟踪了这条线,但我没能让它开始工作。实际上,我很难理解其中的一些概念,因为我对还不熟悉。

几天来,我一直在努力完成这个任务,并且阅读了很多关于验证器、自定义验证器、自定义组件、动态组件等的文章,但都没有结果。我真的很感谢你的帮助。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-02-03 19:50:13

看来我的整个方法太费解了。正确的处理方法在这篇文章中非常详细地解释了,其中还包括源代码:

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

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

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

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48573931

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档