我正在使用 Angular 5 ,我需要创建一个组件(dynform
)来实例化一组自定义组件(dyncompA
,{{1等等)。哪些是dyncompB
决定的,因此它们不会在父模板中声明,而是动态添加。每个子组件都包含从dynform.ngOnInit
(非字符串)派生的MyObjectA
,MyObjectB
等类型的值,我为其实现了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 的新手。
我一直试图让这项工作好几天,并阅读了很多关于验证器,自定义验证器,自定义组件,动态组件等的文章,但无济于事。我非常感谢你的帮助。
答案 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”。