Implementing Mark All Fields as Touched in Angular Reactive Forms

Nov 28, 2025 · Programming · 9 views · 7.8

Keywords: Angular | Reactive Forms | Form Validation

Abstract: This article explores how to programmatically mark all form fields as touched in Angular reactive forms to trigger validation error displays. It delves into the core principles of form validation mechanisms, provides a practical recursive method for traversing form groups, and compares solutions across different Angular versions. With complete code examples and step-by-step explanations, it helps developers optimize user experience during form submission, ensuring validation errors are promptly shown.

Relationship Between Form Validation and Touched State

In Angular reactive forms, validation errors for form controls are typically displayed only when the control is marked as "touched" after user interaction, such as focusing and then blurring. This design prevents error messages from appearing before the user interacts with the form, thereby enhancing user experience. However, during form submission, if the user has not touched all required fields, validation errors may not display, leaving users unaware of what needs correction.

Problem Scenario Analysis

Consider a typical form submission scenario: a user fills out a form and clicks the submit button, but some required fields remain untouched. At this point, even though the form is invalid, validation errors do not appear. To address this, we need to mark all fields as touched upon submission, triggering the validation logic and displaying corresponding errors.

Implementation of Recursively Marking Form Group as Touched

The following recursive function traverses all controls in a form group and marks them as touched. It handles nested form groups, ensuring controls at all levels are properly marked.

private markFormGroupTouched(formGroup: FormGroup) {
  Object.values(formGroup.controls).forEach(control => {
    control.markAsTouched();
    if (control.controls) {
      this.markFormGroupTouched(control);
    }
  });
}

This function first uses Object.values() to obtain an array of all controls in the form group, then iterates through each control. For each control, it calls the markAsTouched() method to mark it as touched. If the control is itself a form group (i.e., contains child controls), it recursively calls the markFormGroupTouched function to ensure controls in nested structures are also processed.

Integrating Touch Marking in Form Submission

Calling the above function in the form submission method ensures all fields are marked as touched before validation logic executes. Below is a complete example showing how to integrate this functionality in the onSubmit method.

onSubmit(form: any): void {
  this.markFormGroupTouched(this.form);
  this.onValueChanged();
  if (this.form.valid) {
    console.log(form);
  }
}

In this code, the markFormGroupTouched function is executed before calling onValueChanged (which updates error messages), ensuring the validation logic runs based on the latest touched state. If the form is valid, the submission data is processed; otherwise, error messages are displayed in the interface.

Simplified Solution in Angular 8 and Later

Starting from Angular 8, the framework provides a built-in method markAllAsTouched() that allows for a more concise implementation of the same functionality. This method automatically recursively marks all descendant controls as touched.

this.form.markAllAsTouched();

Using this method avoids the need for custom recursive functions, reducing code volume and maintenance overhead. Developers should choose the appropriate method based on the Angular version used in their project.

Validation Error Display Mechanism

After marking controls as touched, validation errors are updated via the onValueChanged method. This method iterates through the form errors object, checking each control's touched state and validity to generate corresponding error messages.

onValueChanged(data?: any) {
  if (!this.form) {
    return;
  }
  const form = this.form;
  for (const field in this.formErrors) {
    if (!this.formErrors.hasOwnProperty(field)) {
      continue;
    }
    this.formErrors[field] = '';
    const control = form.get(field);
    if (control && control.touched && !control.valid) {
      const messages = this.validationMessages[field];
      for (const key in control.errors) {
        if (!control.errors.hasOwnProperty(key)) {
          continue;
        }
        this.formErrors[field] += messages[key] + ' ';
      }
    }
  }
}

This method first clears previous error messages, then checks if each control is touched and invalid. If so, it retrieves the corresponding error message from the validationMessages object and updates the formErrors object, ultimately displaying it in the template through data binding.

Best Practices and Considerations

When implementing form validation, it is advisable to separate touch marking logic from business logic to maintain code clarity and maintainability. For complex forms, consider using Angular's built-in validators or custom validators to enhance validation logic. Additionally, ensure that the UI is updated promptly after marking controls as touched to reflect changes in validation status.

Through the methods described, developers can effectively display all validation errors upon form submission, improving user experience and reducing confusion. Choose the appropriate implementation based on project needs and conduct regular testing to ensure the correctness of validation logic.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.