Сравните два значения с помощью настраиваемого валидатора в динамическом c formArray Angular 7 - PullRequest
0 голосов
/ 30 мая 2020

У меня есть группа «additionalForm» в том, что у меня есть formArray, называемый «validations», здесь formArray - это Dynami c, который связывает значения с массивом validtionsField. В массиве validtionsField у меня есть три объекта, в которых у меня есть два значения, которые нужно сравнить, и это минимальная длина и максимальная длина.

ex. Если я ввожу минимальную длину больше, чем максимальную, это должно привести к ошибке.

вот код для вышеуказанной функциональности

import {
  Component,
  OnInit,
  Inject
} from "@angular/core";
import {
  FormControl,
  FormArray,
  FormGroup,
  FormBuilder,
  Validators,
  AbstractControl,
  ValidationErrors,
  NgControlStatus,
  ValidatorFn
} from "@angular/forms";
import {
  MatDialogRef,
  MAT_DIALOG_DATA,
  MatSnackBar
} from "@angular/material";

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.scss"]
})
export class AppComponent implements OnInit {
  validtionsField = [{
      validField: "Min Length",
      type: false,
      fieldType: "input",
      value: 1,
      keyval: "minLength"
    },
    {
      validField: "Max Length",
      type: false,
      fieldType: "input",
      value: 50,
      keyval: "maxLength"
    },
    {
      validField: "DataType",
      type: false,
      fieldType: "dropDown",
      dataTypeList: [],
      dataTypeId: "minLength",
      keyval: "dataTypeId",
      value: 874
    }
  ];

  dataType = [{
      id: 3701,
      localeId: 1,
      tenantId: 1,
      parentCategoryId: null,
      parentContentId: 873,
      name: "Alphabets",
      description: null
    },
    {
      id: 3702,
      localeId: 1,
      tenantId: 1,
      parentCategoryId: null,
      parentContentId: 874,
      name: "Alphanumeric",
      description: null
    }
  ];
  additionalForm: FormGroup = this.fb.group({
    fieldName: ["", [Validators.required]],
    validations: this.fb.array([])
  });

  constructor(public fb: FormBuilder) {}

  ngOnInit() {
    let frmArray = this.additionalForm.get("validations") as FormArray;

    for (let data of this.validtionsField) {
      frmArray.push(this.initSection(data));
    }
  }
  initSection(data) {
    return this.fb.group({
      validField: [data.validField, [Validators.required]],
      type: [data.type, [Validators.required]],
      value: [data.value, [Validators.required]],
      dataTypeList: [this.dataType, [Validators.required]],
      fieldType: [data.fieldType, [Validators.required]],
      validArray: []
    }, {
      validator: this.customValidator
    });
  }

  checkFieldType(data): any {
    return data === "dropDown";
  }

  // trying to access using below functioa to compare values min and max length
  public customValidator(control: AbstractControl): ValidationErrors | null {
    const newValue = control.get("value") ? control.get("value").value : null;
    const values = control.get("value") ? control.get("value").value : [];
    console.log("1 " + newValue);
    console.log(values);
    for (let i = 0, j = values.length; i < j; i++) {
      if (newValue === values[i]) {
        return {
          duplicate2: true
        };
      }
    }
    return null;
  }
}
<form [formGroup]="additionalForm">
  <mat-form-field>
    <input formControlName='fieldName' placeholder="Field Name" required matInput>
  </mat-form-field>
  <div class="row">
    <div class="col-md-12 col-sm-12">
      \
      <div formArrayName="validations">
        <ng-container *ngFor="let validationForm of  additionalForm.controls.validations.controls; let i = index">
          <div class="valid-data" [formGroupName]="i">
            <span>
                  <label>{{validationForm.value.validField }}</label>

                </span>
            <span>
                  <ng-container *ngIf="checkFieldType(validationForm.value.fieldType ); else input">
                    <mat-form-field class="select-dataType">
                      <mat-select required formControlName='value'  placeholder="Datatype">
                        <mat-option *ngFor="let fieldTypeData of validationForm.value.dataTypeList"
                          [value]='fieldTypeData.parentContentId'>
                          {{fieldTypeData.name}}</mat-option>
                      </mat-select>
                    </mat-form-field>
                  </ng-container>
                  <ng-template #input>
                    <mat-form-field>
                      <input required  formControlName='value' pattern= "[0-9]+" matInput>
                    </mat-form-field>
                  </ng-template>
                </span>
            <div *ngIf="validationForm.get('value')?.touched ">
              <div class="error" *ngIf="validationForm.get('value').hasError('required')">
                {{validationForm.value.validField}} is required
              </div>
            </div>
          </div>
        </ng-container>
      </div>
    </div>
  </div>
</form>

выше - это код TS и HTML, а ниже - функция, которую я пытаюсь получить старое и новое значение из управления, но ее сбой, ее выдача me значение из того же поля ввода из той же минимальной длины

/// пробуя эту функцию ниже, чтобы сравнить минимальную и максимальную длину.

public customValidator(control: AbstractControl): ValidationErrors | null {
  const newValue = control.get('value') ? control.get('value').value : null;
  const values = control.get('value') ? control.get('value').value : [];
  console.log("1 " + newValue);
  console.log(values);
  for (let i = 0, j = values.length; i < j; i++) {
    if (newValue === values[i]) {
      return {
        'duplicate2': true
      };
    }
  }
  return null;
}

Пожалуйста, помогите мне сравнить значения из массива форм Dynami c, и здесь все значения, входящие в объект из массива, все привязаны к formcontrolName "value"

вот ссылка на код:

https://stackblitz.com/edit/angular6-material-components-demo-wgtafn

Ответы [ 2 ]

1 голос
/ 02 июня 2020

Поскольку у вас есть два поля minLength и maxLength, проверка которых зависит друг от друга, вы можете добавить валидатор в родительскую группу и использовать настраиваемый ErrorStateMatcher для преобразования ошибок родительской группы в дети. Я также использовал FormGroup вместо FormArray, в этом случае это удобнее.

@Component({...})
export class AppComponent {
  ...

  readonly invalidLengthMatcher: ErrorStateMatcher = {
    isErrorState: () => {
      const control = this.additionalForm.get('validations');
      return control.hasError('invalidLength');
    }
  };

  readonly controlFields = this.validtionsField.map(field => ({
    field,
    control: new FormControl(field.value, Validators.required),
    errorMatcher: this.errorMatcherByFieldId(field.keyval)
  }));

  private readonly controlMap = this.controlFields.reduce((controls, controlField) => {
    controls[controlField.field.keyval] = controlField.control;
    return controls;
  }, {});

  readonly additionalForm = new FormGroup({
    fieldName: new FormControl("", [Validators.required]),
    validations: new FormGroup(this.controlMap, {
      validators: (group: FormGroup) => {
        const [minLength, maxLength] = ['minLength', 'maxLength'].map(fieldId => {
          const control = group.get(fieldId);
          return Number(control.value);
        });

        if (minLength > maxLength) {
          return {
            'invalidLength': true
          };
        } else {
          return null;
        }
      }
    })
  });

  private errorMatcherByFieldId(fieldId: string): ErrorStateMatcher | undefined {
    switch (fieldId) {
      case 'minLength':
      case 'maxLength':
        return this.invalidLengthMatcher;
    }
  }
}
<form [formGroup]="additionalForm">
  <mat-form-field>
    <input formControlName='fieldName' placeholder="Field Name" required matInput>
  </mat-form-field>
  <div class="row">
    <div class="col-md-12 col-sm-12">
      <div formGroupName="validations" >
        <div *ngFor="let controlField of controlFields" class="valid-data">
          <span>
            <label>{{controlField.field.validField}}</label>
          </span>
          <span [ngSwitch]="controlField.field.fieldType">
            <mat-form-field *ngSwitchCase="'dropDown'" class="select-dataType">
              <mat-select required placeholder="Datatype" [formControlName]="controlField.field.keyval">
                <mat-option *ngFor="let fieldTypeData of dataType"
                            [value]='fieldTypeData.parentContentId'>{{fieldTypeData.name}}</mat-option>
              </mat-select>
            </mat-form-field>
            <mat-form-field *ngSwitchCase="'input'">
              <input matInput
                      required
                      type="number"
                      pattern= "[0-9]+"
                      [formControlName]="controlField.field.keyval"
                      [errorStateMatcher]="controlField.errorMatcher">
            </mat-form-field>
          </span>
          ...
        </div>
      </div>
    </div>
  </div>
</form>

StackBlitz

1 голос
/ 01 июня 2020

Вам нужно было прикрепить этот валидатор к массиву, чтобы иметь доступ ко всем элементам управления.

Мой пользовательский валидатор: -

    export function customValidateArray(): ValidatorFn {
    return (formArray:FormArray):{[key: string]: any} | null=>{
      console.log('calling');
      let valid:boolean=true;
      setTimeout(()=>console.log(formArray),200);
      let minIndex = Object.keys(formArray.controls).findIndex((key) => formArray.controls[key].value.validField.toLowerCase()==="min length");
      let minLengthValue = formArray.controls[minIndex].value.value;
      let maxLengthValue = formArray.controls[Object.keys(formArray.controls).find((key) => formArray.controls[key].value.validField.toLowerCase()==="max length")].value.value;
      return minLengthValue < maxLengthValue ? null : {error: 'Min Length Should be less than max length', controlName : formArray.controls[minIndex].value.validField};
    }
  };

Я добавил его в массив ngOnInit из ваш код: -

ngOnInit() {
    let frmArray = this.additionalForm.get("validations") as FormArray;

    for (let data of this.validtionsField) {
      frmArray.push(this.initSection(data));
    }
    frmArray.setValidators(customValidateArray());
  }

Используйте его в шаблоне, например: -

<div class="error" *ngIf="additionalForm.controls.validations.errors && validationForm.value.validField ===  additionalForm.controls.validations.errors.controlName">
                             {{additionalForm.controls.validations.errors.error}}
                      </div>

Рабочий стек: -

https://stackblitz.com/edit/angular6-material-components-demo-p1tpql

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...