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