Reputation: 323
I have a form with my own custom control component:
@Component({
selector: "my-app",
template: `
<form [formGroup]="form">
<app-custom-control formControlName="customControl"></app-custom-control>
</form>
<button (click)="touch()">
Touch!
</button>
`,
styleUrls: ["./app.component.css"]
})
export class AppComponent {
form: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.form = this.fb.group({
customControl: "1"
});
}
touch() {
this.form.markAllAsTouched();
}
}
My custom control component in its turn has another custom control component inside (in my real app it's needed because the outer control component has two modes - read & edit):
@Component({
selector: "app-custom-control",
template: `
<ng-select [ngModel]="value" [items]="items"></ng-select>
`,
styleUrls: ["./custom-control.component.css"],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: CustomControlComponent,
multi: true
}
]
})
export class CustomControlComponent implements ControlValueAccessor, OnInit {
items = ["1", "2"];
value = null;
onChange = (value: any) => {};
onTouched = () => {};
constructor() {}
registerOnChange(fn: any) {
this.onChange = fn;
}
registerOnTouched(fn: () => {}): void {
this.onTouched = fn;
}
writeValue(outsideValue: number) {
this.value = outsideValue;
}
}
It's possible to add NG_VALIDATORS
to CustomControlComponent
and implement how the component should be validated (it can just lift up ng-select errors). But i'm really don't know how to touch inner component (ng-select) when doing this.form.markAllAsTouched()
in the component which wraps the form.
I've tried to do a manipulations in ngDoCheck
lifecycle method but it seems runs too much times what isn't acceptable solution.
Playground: https://stackblitz.com/edit/angular-ivy-u3kdfj
Upvotes: 3
Views: 1699
Reputation: 214017
There is several relevant issues for that in github:
What you can do is to get hold of NgControl for ControlValueAccessor control(outerControl
) and for inner NgModel control(innerControl
):
custom-control.component.ts
export class CustomControlComponent implements ControlValueAccessor, AfterViewInit {
@ViewChild(NgControl) innerNgControl: NgControl;
constructor(private inj: Injector) {}
...
ngAfterViewInit() {
const outerControl = this.inj.get(NgControl).control;
...
}
}
Then you can bypass touched state to inner control by using this trick:
const prevMarkAsTouched = outerControl.markAsTouched;
outerControl.markAsTouched = (...args: any) => {
this.innerNgControl.control.markAsTouched();
prevMarkAsTouched.bind(outerControl)(...args);
};
Upvotes: 5