user1608841
user1608841

Reputation: 2475

Reactive Form is always invalid when using dynamic patterns validation

Reactive Form having dynamic patten validation is always invalid on first load. Even after I edit it still invalid.

This is happening only when I am adding dynamic pattern matching validation.

Here is my use case :

Refer this stackblitz

Below is code

<form [formGroup]="data_form">
    <table class="table table-border">
        <thead>

            <th>
                name
            </th>
            <th>
                age
            </th>
            <th><button class="btn btn-primary ">Save</button></th>
        </thead>
        <tbody>
            <ng-container *ngFor='let item of data;let j = index'>
                <tr>
                    <ng-container *ngFor="let td of keys;let i = index">
                        <ng-container>

                            <td  *ngIf="td !=='isEditable'">
                                {{item[td]}}
                                <input   [formControlName]="getControlName(j,i)" *ngIf="item.isEditable" type="text" name="" id="">
          </td>

                        </ng-container>
                    </ng-container>

                    <td>
                        <button (click)="item.isEditable = true"> Edit</button>
                    </td>
                </tr>
            </ng-container>
        </tbody>
    </table>

</form>

ts code :

import { Component } from "@angular/core";
import { FormControl, FormGroup, Validators } from "@angular/forms";

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  name = "Angular";
  data_form = new FormGroup({});
  public data;
  keys;

  ngOnInit() {
    this.data = [
      {
        name: "Sachin",
        age: 27,

        isEditable: false
      },
      {
        name: "Gopal",
        age: 27,

        isEditable: false
      },
      {
        name: "Pankaj",
        age: 24,

        isEditable: false
      }
    ];
    this.keys = Object.keys(this.data[0]);
    this.data.forEach((element, j) => {
      this.keys.forEach((k, i) => {
        this.data_form.addControl(
          "name_" + j + "_" + i,
          new FormControl(element[k], [Validators.required, Validators.pattern(/^[.\d]+$/)])
        );
      });
    });
  }

  log() {
    console.log(this.data);
  }

  getControlName(j, i) {
    return "name_" + j + "_" + i;
  }
}

Thanks in Advance.

EDIT:

Various Patterns that I have used :

Upvotes: 2

Views: 740

Answers (1)

Owen Kelvin
Owen Kelvin

Reputation: 15083

The problem is actually because the pattern is being used for all the controls. I have refactored your code using FormArray and it seems your pattern work fine

Below is my approach TS File

  constructor(private fb: FormBuilder) {}
  patterns = [
    /^[.\d]+$/,
    /^(yes|no)$/i,
    /^[a-zA-Z0-9 _/]+$/,
    /^(0[1-9]|1[0-2])\/(0[1-9]|1\d|2\d|3[01])\/(19|20)\d{2}$/
  ];
  data = [
    {
      name: "Sachin",
      age: 27,
      isEditable: false
    },
    {
      name: "Gopal",
      age: 27,

      isEditable: false
    },
    {
      name: "Pankaj",
      age: 24,

      isEditable: false
    }
  ];
  keys = [...new Set(this.data.map(item => Object.keys(item)).flat())];
  keyPattern = this.keys.map(item => ({
    key: item,
    pattern: this.patterns.find(pattern =>
      this.data.every(i => pattern.test(i[item]))
    )
  }));
  data_form = this.fb.group({
    persons: this.fb.array(
      this.data.map(item =>
        this.fb.group(
          this.keyPattern.reduce(
            (prev, { key, pattern }) => ({
              ...prev,
              [key]: [
                item[key],
                [Validators.required, Validators.pattern(pattern)]
              ]
            }),
            {}
          )
        )
      )
    )
  });
  get persons(): FormArray {
    return this.data_form.get("persons") as FormArray;
  }

  toggleEdit(j) {
    const currentEditStatus = this.persons.controls[j].get("isEditable").value;
    this.persons.controls[j].get("isEditable").setValue(!currentEditStatus);
  }

HTML

<form [formGroup]="data_form">
    <table class="table table-border">
        <thead>
      <tr>
        <th> name </th>
            <th>age </th>
            <th><button class="btn btn-primary ">Save</button></th>
      </tr>
        </thead>
        <tbody formArrayName='persons'>
            <ng-container *ngFor='let item of persons.controls;let j = index'>
                <tr [formGroupName]='j'>
                    <ng-container *ngIf="!item.value.isEditable; else editable">
                        <td>{{ item.value.name }}</td>
                        <td>{{ item.value.age }}</td>
                    </ng-container>
                    <ng-template #editable>
                        <td><input formControlName='name'></td>
                        <td><input formControlName='age'></td>
                    </ng-template>
                    <td>
                        <button (click)="toggleEdit(j)">
              {{ !item.value.isEditable ? "Edit": "Cancel"}}
            </button>
                    </td>
                </tr>
            </ng-container>
        </tbody>
    </table>

</form>

See below fork Demo

Upvotes: 1

Related Questions