1

The validator works perfectly with required. But I need the unique validator, to check if the labels has duplicate names.

When I switch to unique in inventory.component.ts the table isn't even showing up correctly.
inventory.component.ts:

this.formBuilder.control(item['inventoryName'], RxwebValidators.unique())
// this.formBuilder.control(item['inventoryName'], Validators.required)

https://stackblitz.com/edit/stackblitz-starters-bj8b6n?file=src%2Fapp%2Finventory%2Finventory.component.ts

I noticed, that all other examples on stackoverflow and docs are using RxwebValidators.unique() with an extra key.

    addSkill(){
        let skillsArray = <FormArray>this.employeeFormGroup.controls.skills;
        skillsArray.push(this.getSkillFormGroup());
      }
  
      getSkillFormGroup(){
        // extra skillName key
        return this.formBuilder.group({
          skillName:['',RxwebValidators.unique()]
        })
      }

Is this somehow a requirement? https://docs.rxweb.io/form-validations/unique/validators

Complete code:
inventory.html:

<p>Inventory Form Value: {{ inventoryForm.value | json }}</p>
<p>Form Status: {{ inventoryForm.status }}</p>

<div *ngIf="!inventoryForm.valid">
  <mat-card style="text-align: center; background-color:#ea8288">
    <mat-card-content>Inventory invalid</mat-card-content>
  </mat-card>
</div>
<form [formGroup]="inventoryForm">
  <div class="table-container">
    <table
      mat-table
      class="mat-elevation-z8"
      [dataSource]="inventorySource"
      formArrayName="inventoryFormArr"
    >
      <!-- ref.: https://stackoverflow.com/questions/51150193/angular-material-editable-table-using-formarray -->
      <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
      <tr
        mat-row
        *matRowDef="let row; let i = index; columns: displayedColumns"
      ></tr>
      <!--- Note that these columns can be defined in any order.
                The actual rendered columns are set as a property on the row definition" -->

      <!-- Name Column -->
      <ng-container matColumnDef="inventoryName">
        <th mat-header-cell *matHeaderCellDef>Name</th>
        <td
          mat-cell
          *matCellDef="
            let inventoryFormArr of inventoryFormArr.controls;
            let i = index
          "
        >
          <mat-form-field appearance="outline" style="margin-bottom: -1.25em">
            <input
              matInput
              autocorrect="off"
              autocapitalize="off"
              spellcheck="off"
              type="text"
              required
              type="text"
              [formControlName]="i"
            />
          </mat-form-field>
        </td>
      </ng-container>

      <!-- Quantity Column -->
      <ng-container matColumnDef="quantity">
        <th mat-header-cell *matHeaderCellDef>Quantity</th>
        <td mat-cell *matCellDef="let element">{{ element.quantity }}</td>
      </ng-container>
    </table>
  </div>
</form>

inventory.component.ts:

import { Component } from '@angular/core';
import { FormArray, FormBuilder } from '@angular/forms';
import { Validators } from '@angular/forms';
import { RxwebValidators } from '@rxweb/reactive-form-validators';

export interface myDataArray {
  inventoryName: string;
  quantity: number;
}

const ELEMENT_DATA: myDataArray[] = [
  { inventoryName: 'Cars', quantity: 14 },
  { inventoryName: 'Books', quantity: 49 },
];

@Component({
  selector: 'inventory-table',
  templateUrl: './inventory.component.html',
  styleUrls: ['./inventory.component.scss'],
})
export class InventoryComponent {
  displayedColumns: string[] = ['inventoryName', 'quantity'];

  inventorySource = ELEMENT_DATA;

  inventoryForm = this.formBuilder.group({
    inventoryFormArr: this.formBuilder.array([]),
  });

  constructor(private formBuilder: FormBuilder) {}

  get inventoryFormArr() {
    return this.inventoryForm.get('inventoryFormArr') as FormArray;
  }

  ngOnInit(): void {
    this.inventorySource.forEach((item) => {
      this.inventoryFormArr.push(
        //this.formBuilder.control(item['inventoryName'], RxwebValidators.unique())
        this.formBuilder.control(item['inventoryName'], Validators.required)
      );
    });
  }
}

1 Answer 1

1

Its not able to handle an array of formControls, looks like a bug in the validator, not sure.

In the meantime, as a workaround, you can just create a formArray of formGroups, and the validation seems to work fine!

html

<p>Inventory Form Value: {{ inventoryForm.value | json }}</p>
<p>Form Status: {{ inventoryForm.status }}</p>

<div *ngIf="!inventoryForm.valid">
  <mat-card style="text-align: center; background-color:#ea8288">
    <mat-card-content>Inventory invalid</mat-card-content>
  </mat-card>
</div>
<form [formGroup]="inventoryForm">
  <div class="table-container">
    <table
      mat-table
      class="mat-elevation-z8"
      [dataSource]="inventorySource"
      formArrayName="inventoryFormArr"
    >
      <!-- ref.: https://stackoverflow.com/questions/51150193/angular-material-editable-table-using-formarray -->
      <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
      <tr
        mat-row
        *matRowDef="let row; let i = index; columns: displayedColumns"
      ></tr>
      <!--- Note that these columns can be defined in any order.
                The actual rendered columns are set as a property on the row definition" -->

      <!-- Name Column -->
      <ng-container matColumnDef="inventoryName">
        <th mat-header-cell *matHeaderCellDef>Name</th>
        <td
          mat-cell
          *matCellDef="
            let inventoryFormArr of inventoryFormArr.controls;
            let i = index
          "
          [formGroupName]="i"
        >
          <mat-form-field appearance="outline" style="margin-bottom: -1.25em">
            <input
              matInput
              autocorrect="off"
              autocapitalize="off"
              spellcheck="off"
              type="text"
              required
              type="text"
              formControlName="inventoryName"
            />
          </mat-form-field>
        </td>
      </ng-container>

      <!-- Quantity Column -->
      <ng-container matColumnDef="quantity">
        <th mat-header-cell *matHeaderCellDef>Quantity</th>
        <td mat-cell *matCellDef="let element">{{ element.quantity }}</td>
      </ng-container>
    </table>
  </div>
</form>

ts

import { Component } from '@angular/core';
import { FormArray, FormBuilder, FormGroup } from '@angular/forms';
import { Validators } from '@angular/forms';
import { RxwebValidators } from '@rxweb/reactive-form-validators';

export interface myDataArray {
  inventoryName: string;
  quantity: number;
}

const ELEMENT_DATA: myDataArray[] = [
  { inventoryName: 'Cars', quantity: 14 },
  { inventoryName: 'Books', quantity: 49 },
];

@Component({
  selector: 'inventory-table',
  templateUrl: './inventory.component.html',
  styleUrls: ['./inventory.component.scss'],
})
export class InventoryComponent {
  displayedColumns: string[] = ['inventoryName', 'quantity'];

  inventorySource = ELEMENT_DATA;

  inventoryForm = this.formBuilder.group({
    inventoryFormArr: this.formBuilder.array([]),
  });

  constructor(private formBuilder: FormBuilder) {}

  get inventoryFormArr() {
    return this.inventoryForm.get('inventoryFormArr') as FormArray;
  }

  ngOnInit(): void {
    this.inventorySource.forEach((item) => {
      this.inventoryFormArr.push(
        //this.formBuilder.control(item['inventoryName'], RxwebValidators.unique())
        new FormGroup({
          inventoryName: this.formBuilder.control(item['inventoryName'], [
            Validators.required,
            RxwebValidators.unique(),
          ]),
        })
      );
    });
  }
}

stackblitz

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.