I'm trying to create a form where the user can select multiple colors and then submit the form.
Code
colors: Array<any> = [
{ description: 'White', value: 'White' },
{ description: 'Black', value: 'Black' },
{ description: 'Blue', value: 'Blue' },
{ description: 'Green', value: 'Green' },
{ description: 'Yellow', value: 'Yellow' },
{ description: 'Red', value: 'Red' },
];
constructor(
private formBuilder: FormBuilder
) {
this.fromInit();
}
ngOnInit(): void {}
fromInit() {
this.form = this.formBuilder.group({
colors: this.formBuilder.array([], [Validators.required]),
});
}
onCheckChange(event) {
const formArray: FormArray = this.form.get(
'colors'
) as FormArray;
if (event.target.checked) {
formArray.push(new FormControl(event.target.value));
} else {
let i: number = 0;
formArray.controls.forEach((ctrl: FormControl) => {
if (ctrl.value == event.target.value) {
formArray.removeAt(i);
return;
}
i ;
});
}
}
invalidColorsMessage() {
if (this.form.controls['colors'].errors?.required)
return "You must choose a color";
}
HTML
<div
*ngIf="this.form.controls['colors'].touched &&
this.form.controls['colors'].invalid">
{{invalidColorsMessage()}}</div>
The issue that I encountered that property 'touched' of the this.form.controls['colors'] is always false. no matter if I select a few checkboxes or don't select anything the value of 'touched' is always false.
I don't really understand why it's always false when I check and uncheck the checkboxes.
the expected result is to show an error message whenever someone checks and unchecks the boxes and leaving all empty.
I would like to know why the value of this.form.controls['colors'].touched is always false no matter what. So I tried to search for the answer online but unfortunately with no success in finding the solution.
Here is a demo of the checkboxes : https://stackblitz.com/edit/angular-ivy-s8rc6j?file=src/app/app.component.html
thanks
CodePudding user response:
Update stackblitz:
set
isTouched = false;to global variablecheck
*ngIf=="isTouched && this.form.controls['colors'].invalid"added
this.isTouched = true;inonCheckChange(event) {}
This is Github closed issue
The touched property reflects whether a form control has been blurred, not changed.
The form touched would never return true because the blurred event only happened at input element.
You could change your code from:
<div class="invalid-input-message" *ngIf="this.form.controls['colors'].touched && this.form.controls['colors'].invalid">
To: (remove this.form.controls['colors'].touched condition)
<div class="invalid-input-message" *ngIf="this.form.controls['colors'].invalid">
CodePudding user response:
You could use a custom validator, good thing, it is reusable! We attach it to the formarray and check the length of the array, if length is at least 1, we markAllAsTocuhed, therefore you can keep your touched in the template. So the function would look like:
export function validate(ctrls: FormArray): ValidationErrors | null {
if (ctrls.value.length) {
ctrls.markAllAsTouched();
return null;
}
return { notValid: true };
}
You can put this in a service or somewhere where other components can import it if you want it to be reusable.
OK, so now let's attach this to the formarray:
fromInit() {
this.form = this.formBuilder.group({
colors: this.formBuilder.array([], { validators: validate }),
});
}
And then in the template you check if error exist and if it's touched:
<div *ngIf="form.get('colors').hasError('notValid') && form.get('colors').touched">
Please choose at least one
</div>
Your forked STACKBLITZ
