What I want
If a user click on the submit button and did not fill all the inputs, I will mark all input as touched, since there is a required validator on the form, the empty inputs are supposed to get a red border.
Problem
When I mark all the inputs of the form as touched, the custom input does not change, the classes were not added:
Only the basic input have the classes, here is the result of the html, only the basic input is showing the red borders:
Input component
import {AfterContentChecked, Component, Input, OnInit, Optional, Self} from '@angular/core';
import {ControlValueAccessor, NgControl, UntypedFormControl, UntypedFormGroup} from '@angular/forms';
@Component({
selector: 'app-input',
templateUrl: './input.component.html',
styleUrls: ['./input.component.css'],
providers: [],
})
export class InputComponent implements ControlValueAccessor, OnInit {
group = new UntypedFormGroup({
input: new UntypedFormControl(''),
});
touched: boolean = false;
@Input() label: string = '';
private ngControl: NgControl;
constructor(@Optional() @Self() ngControl: NgControl) {
this.ngControl = ngControl;
if (this.ngControl) {
this.ngControl.valueAccessor = this;
}
}
_value: string = '';
get value(): string {
return this._value;
}
@Input()
set value(value: string) {
this._value = value;
this.input.setValue(value, {emitEvent: false});
}
_disabled: boolean = false;
get disabled(): boolean {
return this._disabled;
}
@Input()
set disabled(disabled: boolean) {
this._disabled = disabled;
if (disabled) {
this.input.disable();
} else {
this.input.enable();
}
}
get input() {
return this.group.get('input') as UntypedFormControl;
}
ngOnInit(): void {
if (this.ngControl !== null) {
this.ngControl.control?.statusChanges.subscribe((status) => {
if (status === 'INVALID') {
this.input.markAsDirty();
this.input.markAsTouched();
this.input.setErrors({incorrect: true});
}
});
} else {
console.warn('no formcontrol');
}
}
onTouched = () => {
};
writeValue(value: string): void {
this.input.setValue(value, {emitEvent: false});
}
registerOnChange(fn: any): void {
this.input.valueChanges.subscribe((value) => {
this.markAsTouched();
fn(value);
});
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
markAsTouched() {
if (!this.touched) {
this.onTouched();
this.touched = true;
}
}
setDisabledState?(isDisabled: boolean): void {
if (isDisabled) {
this.input.disable();
} else {
this.input.enable();
}
}
}
<span [formGroup]="group">
<label [innerHTML]="label" ></label>
<input
class="w-full" formControlName="input"
ngDefaultControl
type="text">
</span>
App omponent
import { Component } from '@angular/core';
import {UntypedFormControl, UntypedFormGroup, Validators} from "@angular/forms";
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'test-input';
myForm = new UntypedFormGroup({
inputBasic: new UntypedFormControl('',[Validators.required]),
inputCustom: new UntypedFormControl('',[Validators.required]),
})
submit(){
if(this.myForm.valid){
console.log('valid')
}else{
this.myForm.markAllAsTouched();
// If I add the next line the input is updated and the style is good
// this.myForm.get('inputCustom')?.setErrors({errorMessage:'Error'})
}
}
}
<form [formGroup]="myForm" (submit)="submit()">
<div>
Basic input <input formControlName="inputBasic"/>
</div>
<div>
<app-input label="Custom input" formControlName="inputCustom"></app-input>
</div>
<button type="submit">Submit</button>
</form>
CSS in both component
.ng-invalid.ng-touched{
border-color:red;
}