Я правильно строю массив форм следующим образом:

this.items.forEach(element => {
                    (<FormArray>this.myFormFixtures.get('fixtures')).push(this.fBuilder.group({
                        id: [element.id],
                        name: [element.name, Validators.required],
                        description: [element.description],
                        startDateTime: this.buildTimeDetails(element.startDateTime),
                        venueId: [element.venueId, Validators.required],
                        participants: this.buildParticipants(element.participants)
                    }));
                });
                this.myFormFixtures.valueChanges
                    .subscribe(formData => this.checkFixturesFormValidity(formData));

Ключ «Участники» генерируется в виде массива с двумя идентификаторами с нулевым значением, например:

[
{id: null},
{id: null}
]

По умолчанию 2 идентификатора не являются обязательными. Но я хочу установить оба идентификатора участников formControlNames как обязательные при получении значения.

Пока моя функция "checkFixturesFormValidity" выглядит так, но по какой-то причине проверка не выполняется:

checkFixturesFormValidity(formData)
    {
        console.log(formData);

        const control = <FormArray>this.myFormFixtures.controls['fixtures'];

        for (let i = 0; i < control.length; i++) {

            const participantsControl = (<FormArray>this.myFormFixtures.controls['fixtures']).at(i).get('participants') as FormArray;

                if ((((<any>this.myFormFixtures.controls['fixtures']).at(i).controls['participants'].at(0).get('id').value) != null) || (((<any>this.myFormFixtures.controls['fixtures']).at(i).controls['participants'].at(1).get('id').value) != null))
                {
                    (<any>this.myFormFixtures.controls['fixtures']).at(i).controls['participants'].at(0).get('id').setValidators([Validators.required]);
                    (<any>this.myFormFixtures.controls['fixtures']).at(i).controls['participants'].at(1).get('id').setValidators([Validators.required]);
                }


        }

    }
1
Andrew Junior Howard 29 Июл 2017 в 21:26
Это довольно длинные пути, поэтому я не буду пытаться выяснить, что с ними не так;) Но вам, вероятно, нужно вызвать updateValueAndValidity(). Если это не помогает, создайте плункер, демонстрирующий проблему.
 – 
AJT82
29 Июл 2017 в 22:10
Я посмотрю на создание плункера :) Я звонил updateValueAndValidity (), но он продолжает вылетать из браузера, говоря, что превышен стек вызовов maxmium
 – 
Andrew Junior Howard
29 Июл 2017 в 22:17
Да, это, вероятно, из-за этой строки: .subscribe(formData => this.checkFixturesFormValidity(formData)) checkFixturesFormValidity() вызывается каждый раз, когда что-то происходит в значениях формы, и, поскольку вы используете updateValueAndValidity() в этом, "происходят изменения", что означает subscribe запускается снова, и так продолжается до тех пор, пока браузер не выйдет из строя;) Вместо этого вы должны рассмотреть простое событие change для поля.
 – 
AJT82
29 Июл 2017 в 22:25
1
Ах, спасибо. Я сначала попробую это, а если нет, пришлю плункер. Спасибо за вашу помощь :)
 – 
Andrew Junior Howard
29 Июл 2017 в 22:32
1
Без проблем. Если других ошибок нет, я думаю, что updateValueAndValidity решит вашу проблему. В качестве примечания, даже если это вопрос мнения, я стал полагаться на события вместо того, чтобы использовать valueChanges, если у меня нет какой-либо конкретной причины. С событиями у вас есть более жесткий контроль над тем, что и когда запускать. При использовании valueChanges вы не можете контролировать его так жестко, и с точки зрения производительности это может стать тяжелым. Даже при инициализации компонента valueChanges будет запускаться x количество раз, что обычно совершенно не нужно :)
 – 
AJT82
29 Июл 2017 в 22:48

1 ответ

Лучший ответ

Мне удалось несколько сократить все эти длинные пути свойств, поскольку в итерациях мы можем передавать, например, объект, который вы назвали в своем *ngFor, так что в методах нам не нужно начинать с this.myFormFixtures.controls....

Итак, что мы сделали здесь, так это установили событие изменения для вашего выбора. Поскольку мы знаем, что когда пользователь делает выбор, если значение отличается от null, мы устанавливаем Validators.required.

Итак, содержимое tbody будет выглядеть так (сокращенно):

<tr *ngFor="let fixture of myFormFixtures.controls.fixtures.controls; 
            let c=index" [formGroupName]="c">
  <td><input type="text" formControlName="name" /></td>
  <td class="teamColumn">
    <div formArrayName="participants">
      <div *ngFor="let thisParticipant of fixture.controls.participants.controls; 
                   let i=index" [formGroupName]="i">
        <!-- We pass the current formgroup and value of dropdown, 
             which is null, 1 or 2 -->
        <select formControlName="id 
           (change)="doSomething(fixture.controls.participants, 
                     thisParticipant.controls.id.value)">
          <option [ngValue]=null>Please select</option>
          <option [ngValue]="1">Team Red</option>
          <option [ngValue]="2">Team Blue</option>
        </select>
      </div>
    </div>
  </td>
</tr>

Тогда doSomething() будет выглядеть так:

// form array and chosen value
doSomething(formArr, val) {
  for (let x of formArr.controls) {
    x.controls.id.setValidators([Validators.required]);
    x.controls.id.updateValueAndValidity();
  }      
}

Ваша демонстрация: https://plnkr.co/edit/hovobDwYUgH3C6fbe7eB?p=preview

PS, это не учитывается, если пользователь отменяет выбор значения.

3
AJT82 30 Июл 2017 в 12:02
Спасибо за это, это круто! Если мне нужно изменить его, если пользователь снова отменит выбор обоих раскрывающихся списков, что будет лучшим подходом? Прокрутите массив в методе doSomething, и если все значения в массиве равны нулю, очистите валидаторы, иначе установите валидаторы? Также, по вашему мнению, лучше избегать «valueChanges»?
 – 
Andrew Junior Howard
30 Июл 2017 в 15:45
Так как я прокомментировал ваш вопрос, я объяснил, почему я предпочитаю события изменения. Особенно в этом случае вы заметили, что столкнулись с проблемами с обнаружением изменений, subscribe и вызовом метода :) Что касается вашего другого вопроса, вот одна возможность решить, что, если оба не отмечены, валидатор будет удален: < a href = "https://plnkr.co/edit/EFUMbA2oKgHn5aZUWfOT?p=preview" rel = "nofollow noreferrer"> plnkr.co/edit/EFUMbA2oKgHn5aZUWfOT?p=preview
 – 
AJT82
30 Июл 2017 в 16:32