In my Angular app I have a form with dynamically changes input components. All dynamic components are implements ControlValueAccessor.
If you input value in Input and next change component (click button "change component to number"), then ngControl don't change his reference valueAccessor to new component. And my new Input component will not change my model. What am I doing wrong ? I have a example in a stackblitz. And I have an example code.
My dynamic form component:
@Component({
selector: "app-form-control-outlet",
template: ``,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => FormControlOutletComponent),
multi: true
}
]
})
export class FormControlOutletComponent implements OnChanges {
@Input() component = CustomStringInputComponent;
componentRef: ComponentRef<any>;
constructor(
public injector: Injector,
private container: ViewContainerRef,
private resolver: ComponentFactoryResolver,
private viewContainerRef: ViewContainerRef
) {}
public ngOnChanges(changes: SimpleChanges): void {
const factory = this.resolver.resolveComponentFactory(this.component);
const componentFactory = this.resolver.resolveComponentFactory(
this.component
);
if (this.container.length > 0) {
this.container.clear();
}
this.componentRef = this.viewContainerRef.createComponent(componentFactory);
const ngControl = this.injector.get(NgControl);
ngControl.valueAccessor = this.componentRef.instance;
}
}
And one of my dynamic Input
@Component({
selector: "app-custom-input",
template: `
<input
[(ngModel)]="value"
(ngModelChange)="onValueChange($event)"
(blur)="onInputBlurred()"
/>
`,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CustomStringInputComponent),
multi: true
}
]
})
export class CustomStringInputComponent implements ControlValueAccessor {
public value: string;
public onChange: (value: string) => void;
public onTouched: () => void;
public writeValue(value: string): void {
this.value = value;
}
public registerOnChange(fn: (value: string) => void): void {
this.onChange = fn;
}
public registerOnTouched(fn: () => void): void {
this.onTouched = fn;
}
public onValueChange(value: string): void {
this.writeValue(value);
this.onChange(value);
}
public onInputBlurred(): void {
this.onTouched();
}
}
This happens because when you change valueAccessor Angular doesn't register onChange and onTouched events on that new component again.
What you can try as workaround is to register those events manually:
const ngControl = this.injector.get(NgControl);
const valueAccessor = ngControl.valueAccessor as any;
if (valueAccessor && valueAccessor.onChange) {
this.componentRef.instance.registerOnChange(valueAccessor.onChange);
this.componentRef.instance.registerOnTouched(valueAccessor.onTouched);
}
ngControl.valueAccessor = this.componentRef.instance;
Forked Stackblitz
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With