我使用的是角5,我需要创建一个组件(dynform)来实例化一组定制组件(dyncompA、dyncompB等)。哪些是在dynform.ngOnInit中决定的,所以它们没有在父模板中声明,而是动态添加。每个子组件都包含一个类型为MyObjectA、MyObjectB等的值,该值来自于我实现ControlValueAccessor接口的MyObjectAbstract (而不是字符串)。
我遇到的问题是,父表单从来没有被通知过子组件的有效性状态,或者它们更改的(!原始的)状态。我的自定义验证器也没有被调用过。此外,子组件不会从AbstractControl接收它的属性值。我可以看到,ComponentA的registerOnChange从未被调用过,也没有人订阅组件的valueChange @Output事件。然而,如果我在模板中静态地使用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
}
}不管怎么说,这是个概念。典型的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并不真正适用于动态加载的组件;这就是为什么我也实现了valueChange @Output。但问题似乎来自这样一个事实,即ngForm验证逻辑与@FormControlName指令相关联,在创建它之前,我不知道如何将它应用/生成到动态控件。
我跟踪了这条线,但我没能让它开始工作。实际上,我很难理解其中的一些概念,因为我对角还不熟悉。
几天来,我一直在努力完成这个任务,并且阅读了很多关于验证器、自定义验证器、自定义组件、动态组件等的文章,但都没有结果。我真的很感谢你的帮助。
发布于 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内部“。
https://stackoverflow.com/questions/48573931
复制相似问题