skip to Main Content

I have a relative complex form with a multiple fields.
Some of the fields a building the logical blocks, and I want to highlight the surrounding div if any of the included fields are invalid. What could be the best approach to implement it?

Currently I try to implement in following way, but unfortunately stuck in the marked place

import { Component } from '@angular/core';
import {FormBuilder, Validators} from "@angular/forms";

@Component({
  selector: 'app-root',
  template: `
    <form [formGroup]="form">
      <input formControlName="name">
      <div class="div-address" [class.div-error]="DivHasError(divAddress)" #divAddress>
        <div class="div-text">Address</div>
        <input formControlName="addressType">
        <div formGroupName="address">
          <div>
            <input formControlName="street">
          </div>
          <div>
            <input formControlName="city">
          </div>
        </div>
      </div>
    </form>
  `,
  styles: [
    `
      input.ng-invalid {border-color: red;}
      .div-error .div-text {color: red;}
    `
  ]

})
export class AppComponent {
  protected form = this.fb.group({
    name: ['', Validators.required],
    addressType: ['office', Validators.required],
    address: this.fb.group({
      street: ['', Validators.required],
      city: ['', Validators.required],
    })
  });
  constructor(private fb: FormBuilder) {
  }

  DivHasError(divElement: HTMLDivElement): boolean {
    //TODO: How to find out which fields are included and if they are valid in more generic way?
    if (! (this.form.get(['addressType'])?.valid ?? true)) return true;
    if (! (this.form.get(['address','street'])?.valid ?? true)) return true;
    if (! (this.form.get(['address','city'])?.valid ?? true)) return true;
    return false;
  }
}

Obviously I want to have a generic solution, not to list all the fields manually in the method "DivHasError". How is it best way possible?

2

Answers


  1. Chosen as BEST ANSWER

    Ok, I have solved it in a pretty straightforward way. Below is a helper function that checks if any fields in the div are invalid.

    function ExtractControls(form: FormGroup, elements:Element[], path: string[], controls: AbstractControl[]) {
      for (let child of elements) {
        const formGroupName = child.getAttribute('formGroupName');
        const formControlName = child.getAttribute('formControlName');
        const fieldPath = formGroupName ? [...path, formGroupName] : path;
        if (formControlName) {
          const field = form.get([...fieldPath, formControlName]);
          if (field) {
            controls.push(field)
          }
        }
        if (child.children.length > 0) {
          ExtractControls(form, Array.from(child.children), fieldPath, controls);
        }
      }
    }
    
    export function DivHasError(formGroup: FormGroup, div:HTMLDivElement): boolean {
      const controls: AbstractControl[] = [];
      ExtractControls(formGroup, Array.from(div.children), [], controls);
      for (let control of controls) {
        if (!control.valid) {
          return true;
        }
      }
      return false;
    }
    

    and HTML

    <div class="div-address" [class.div-error]="DivHasError(form, divAddress)" #divAddress>
    

    I am not sure about performance, I feel it is a bit too much logic for a validation, but for a first glance looks OK.


  2. why not simply ?

    div.ng-invalid, input.ng-invalid {border-color: red;}

    basically on all invalid fields Angular will add an ng-invalid class, so just list them in your CSS

    https://angular.io/guide/form-validation
    => look at Control status CSS classes

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search