Угловая перекрестная проверка FormArray между свойствами объекта - PullRequest
0 голосов
/ 05 декабря 2018

У меня есть Formgroup с Formarray внутри.

Это структура:

myForm = this.fb.group(
        {
            title: ["", [Validators.required, Validators.minLength(4)]],
            pairs: this.fb.array(
                this.fPairs.map(f =>
                    this.fb.group({
                        grade: [],
                        value: []
                    })
                )
            )
        }
    );

мой FormArray, который отображается, выглядит следующим образом onInit:

fPairs: Array<pairs> = [
        {grade: 0, value: 0},
        {grade: 0, value: 0},
        {grade: 0, value: 0},
        {grade: 0, value: 0}
    ];

Чего я хочу достичь, так как каждое свойство каждого объекта этого FormArray является полем ввода в моей форме, мне нужна проверка, которая делает это:

Свойства объекта в индексе 0, должны иметь значения BIGGERчем следующий индекс.

, поэтому в myForm,

pairs[0].score > pairs[1].score
pairs[1].score > pairs[2].score
pairs[2].score > pairs[3].score

то же самое относится и к свойству "value".

Как правильно реализовать реальный валидатор (введите ValidatorFn) для этого formArray?

Пока мне удалось создать только функцию, которая проверяет каждое поле, сравнивает его с предыдущим и следующим, если значения не соответствуют правилам, я вручную устанавливаю ошибку с setErrors()

Эта функция находится в подписке ValueChanges(), поэтому, когда значение в этом formArray изменяется, она проверяет его с моей функцией

Есть ли лучший способ?

Здесьstackblizt (подписка valueChanges не работает должным образом, она будет обновляться только при записи в следующем поле, вы увидите, что я имею в виду в stackblitz)

https://stackblitz.com/edit/angular-mat-formfield-flex-layout-x9nksb

спасибо

1 Ответ

0 голосов
/ 05 декабря 2018

Итак, через некоторое время (извините за задержку) я сделал stackblitz , воспроизводящий ваш минимальный пример, и я сделал для вас валидатор.Код находится в конце моего ответа.

Чтобы кратко объяснить вам: перекрестная проверка должна быть сделана в родительском.Родителем может быть либо группа форм, либо массив форм.В вашем случае это будет массив форм (массив, содержащий все ваши оценки).

Затем ошибки добавляются непосредственно в массив форм, что делает его недействительным при выполнении условия.

Как видите, органы управления не имеют ни малейшего представления об их ошибках: я сделал это добровольно, чтобы я не работал на вас.Целью для меня было показать вам, как сравнивать два поля и устанавливать ошибки формы соответственно.

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

Код объясняется самостоятельно, но если у вас есть вопросы, не стесняйтесь их задавать!

(PS: ошибки отображаются в нижнем колонтитуле страницы stackblitz, если вы хотите их видеть)

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, FormArray, ValidatorFn } from '@angular/forms';

@Component({
  selector: 'my-app',
  template: `
<form novalidate [formGroup]="form" fxLayout="column" fxLayoutGap="24px">
    <div formArrayName="pairs" fxLayout="row" fxLayoutGap="12px" *ngFor="let pair of form.get('pairs').controls; let i = index">
        <ng-container [formGroupName]="i">
            <mat-form-field fxFlex="50%">
                <input matInput type="text" formControlName="grade" placeholder="Grade for {{ i }}">
      </mat-form-field>
      <mat-form-field fxFlex="50%">
        <input matInput type="text" formControlName="value"  placeholder="Score for {{ i }}">
      </mat-form-field>
    </ng-container>
  </div>
</form>

<p>The form is {{ form.invalid ? 'invalid' : 'valid' }}</p>
<p>The pairs group is {{ form.get('pairs').invalid ? 'invalid' : 'valid' }}</p>

<p>Errors on the form : {{ form.errors | json }}</p>
<p>Errors on the group : {{ form.get('pairs').errors | json }}</p>
`,
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  form: FormGroup;

  // Create a dataset
  data = [
    { grade: 6, value: 0 },
    { grade: 5, value: 0 },
    { grade: 4, value: 0 },
    { grade: 3, value: 0 },
  ];

  constructor(private fb: FormBuilder) { }

  ngOnInit(): void {
    // Create a form group
    this.form = this.fb.group({
      // Create a form array made of form groups
      pairs: this.fb.array(this.data.map(item => this.fb.group(item)))
    });

    // Add validators (optional, used to split the code logic)
    this.addValidators();
  }

  addValidators() {
    // Get the form array and append a validator (again code split)
    (this.form.get('pairs') as FormArray).setValidators(this.formArrayValidator());
  }

  // Form validator
  formArrayValidator(): ValidatorFn {
    // The validator is on the array, so the AbstractControl is of type FormArray
    return (group: FormArray) => {
      // Create an object of errors to return
      const errors = {};
      // Get the list of controls in the array (which are FormGroups)
      const controls = group.controls;
      // Iterate over them
      for (let i = 1; i < controls.length; i++) {
        // Get references to controls to compare them (split code again)
        const valueControl = controls[i].get('value');
        const previousValueControl = controls[i - 1].get('value');

        // if error, set array error
        if (valueControl.value > previousValueControl.value) {
          // array error (sum up of all errors)
          errors[i + 'greaterThan' + (i - 1)] = true;
        }
      }

      // return array errors ({} is considered an error so return null if it is the case)
      return errors === {} ? null : errors;
    }
  }
}
...