Реактивная форма с использованием NgFor

Я пытался понять это некоторое время, и я застрял. Мой подход кажется неэффективным и неправильным.

Что я делаю, так это генерирую значения моей формы с помощью * ngFor, я динамически создаю элементы управления формой для ответов, а также для вопросов.

Проблема в том, что при отправке мне нужны ответы, связанные с вопросами, чтобы я мог перейти к следующему экрану. Вопросы, сгенерированные из *ngFor, сортируются по установленному флажку, если selected = true; затем поднимите вопрос наверх.

Проблема в том, что при отправке pvqForm передал formControls, мне нужно связать ответ с вопросом, с которым он был связан. FormControls имеют префикс {{i}} --> index, однако этот индекс стоит после сортировки. Таким образом, индекс 19 может не быть question_id = '19'.

Объект, который я пытаюсь достичь onSubmit:

{
   question_id: "#",
   EN: "Question - #",
   FR: "Question - #",
   answer: "",
   isSelected: true
}

Я могу сохранить все вопросы в массиве и просто использовать question_id для сопоставления вопросов. Однако как я могу добиться совпадения question_id вместе с ответом?

Объект, с которого начинается *ngFor, имеет вид...

{
  question_id: "1",
  EN: "Question 1 - EN",
  FR: "Question 1 - FR",
  isSelected: true
},
{ 
  question_id: "2",
  EN: "Question 2 - EN",
  FR: "Question 2 - FR",
  isSelected: false
}

Мой компонент:

export class FpPVQ implements OnInit {
  private questions: any;
  private lang: string;
  private counter: number = 0;
  private checkedLimit: number = 5;
  private updateMode: boolean = false;
  private createMode: boolean = false;
  private deadlinePassed: boolean = false;
  private previousScreen: string = "";
  private flowA: boolean = false;
  private flowB: boolean = false;
  private flowC: boolean = false;
  private flowD: boolean = false;
  private pvqForm: FormGroup;
  private answer_0: FormControl;
  private answer_1: FormControl;
  private answer_2: FormControl;
  private answer_3: FormControl;
  private answer_4: FormControl;
  private answer_5: FormControl;
  private answer_6: FormControl;
  private answer_7: FormControl;
  private answer_8: FormControl;
  private answer_9: FormControl;
  private answer_10: FormControl;
  private answer_11: FormControl;
  private answer_12: FormControl;
  private answer_13: FormControl;
  private answer_14: FormControl;
  private answer_15: FormControl;
  private answer_16: FormControl;
  private answer_17: FormControl;
  private answer_18: FormControl;
  private answer_19: FormControl;
  private question_0: FormControl;
  private question_1: FormControl;
  private question_2: FormControl;
  private question_3: FormControl;
  private question_4: FormControl;
  private question_5: FormControl;
  private question_6: FormControl;
  private question_7: FormControl;
  private question_8: FormControl;
  private question_9: FormControl;
  private question_10: FormControl;
  private question_11: FormControl;
  private question_12: FormControl;
  private question_13: FormControl;
  private question_14: FormControl;
  private question_15: FormControl;
  private question_16: FormControl;
  private question_17: FormControl;
  private question_18: FormControl;
  private question_19: FormControl;


  private answerControlArr: Array<FormControl> = [];
  private questionsArr: Array<any> = [];



@ViewChild(DirtyModalComponent) public readonly dirtyModal: DirtyModalComponent;

  constructor(private _pvqStepUp: PVQStepUpService, private _langToggle: ErrorToggleService, private router: Router) { }

  ngOnInit() {
    this.lang = window['appdata'].apiUserLanguage;
    console.log('fpPVQ:: pvqStepUpData --> {}, ', this._pvqStepUp.data);
    if (this._pvqStepUp.data != null) {
      this.previousScreen = this._pvqStepUp.data.previousScreen;
      this.questions = this._pvqStepUp.data.pvqs;
      console.log('4176 FPVQ:: flow --> {}', this._pvqStepUp.data.flow);
      switch(this._pvqStepUp.data.flow){
        case "A": this.flowA = true; break;
        case "B": this.flowB = true; break;
        case "C": this.flowC = true; break;
        case "D": this.flowD = true; break;
      }
      this.questions.forEach(object => {
        this.questionsArr.push([object.question_EN, object.question_FR, object.answer]);
        console.log(object.question_id, object.EN, object.FR);
      })
      if (this._pvqStepUp.data.deadlinePassed)
        this.deadlinePassed = true;
    }

this.answer_0 = new FormControl('', Validators.required);
this.answer_1 = new FormControl('', Validators.required);
this.answer_2 = new FormControl('', Validators.required);
this.answer_3 = new FormControl('', Validators.required);
this.answer_4 = new FormControl('', Validators.required);
this.answer_5 = new FormControl('', Validators.required);
this.answer_6 = new FormControl('', Validators.required);
this.answer_7 = new FormControl('', Validators.required);
this.answer_8 = new FormControl('', Validators.required);
this.answer_9 = new FormControl('', Validators.required);
this.answer_10 = new FormControl('', Validators.required);
this.answer_11 = new FormControl('', Validators.required);
this.answer_12 = new FormControl('', Validators.required);
this.answer_13 = new FormControl('', Validators.required);
this.answer_14 = new FormControl('', Validators.required);
this.answer_15 = new FormControl('', Validators.required);
this.answer_16 = new FormControl('', Validators.required);
this.answer_17 = new FormControl('', Validators.required);
this.answer_18 = new FormControl('', Validators.required);
this.answer_19 = new FormControl('', Validators.required);

this.question_0 = new FormControl();
this.question_1 = new FormControl();
this.question_2 = new FormControl();
this.question_3 = new FormControl();
this.question_4 = new FormControl();
this.question_5 = new FormControl();
this.question_6 = new FormControl();
this.question_7 = new FormControl();
this.question_8 = new FormControl();
this.question_9 = new FormControl();
this.question_10 = new FormControl();
this.question_11 = new FormControl();
this.question_12 = new FormControl();
this.question_13 = new FormControl();
this.question_14 = new FormControl();
this.question_15 = new FormControl();
this.question_16 = new FormControl();
this.question_17 = new FormControl();
this.question_18 = new FormControl();
this.question_19 = new FormControl();

this.answerControlArr.push(this.answer_0);
this.answerControlArr.push(this.answer_1);
this.answerControlArr.push(this.answer_2);
this.answerControlArr.push(this.answer_3);
this.answerControlArr.push(this.answer_4);
this.answerControlArr.push(this.answer_5);
this.answerControlArr.push(this.answer_6);
this.answerControlArr.push(this.answer_7);
this.answerControlArr.push(this.answer_8);
this.answerControlArr.push(this.answer_9);
this.answerControlArr.push(this.answer_10);
this.answerControlArr.push(this.answer_11);
this.answerControlArr.push(this.answer_12);
this.answerControlArr.push(this.answer_13);
this.answerControlArr.push(this.answer_14);
this.answerControlArr.push(this.answer_15);
this.answerControlArr.push(this.answer_16);
this.answerControlArr.push(this.answer_17);
this.answerControlArr.push(this.answer_18);
this.answerControlArr.push(this.answer_19);

this.pvqForm = new FormGroup({
  answerControl0: this.answer_0,
  answerControl1: this.answer_1,
  answerControl2: this.answer_2,
  answerControl3: this.answer_3,
  answerControl4: this.answer_4,
  answerControl5: this.answer_5,
  answerControl6: this.answer_6,
  answerControl7: this.answer_7,
  answerControl8: this.answer_8,
  answerControl9: this.answer_9,
  answerControl10: this.answer_10,
  answerControl11: this.answer_11,
  answerControl12: this.answer_12,
  answerControl13: this.answer_13,
  answerControl14: this.answer_14,
  answerControl15: this.answer_15,
  answerControl16: this.answer_16,
  answerControl17: this.answer_17,
  answerControl18: this.answer_18,
  answerControl19: this.answer_19,
  questionControl0: this.question_0,
  questionControl1: this.question_1,
  questionControl2: this.question_2,
  questionControl3: this.question_3,
  questionControl4: this.question_4,
  questionControl5: this.question_5,
  questionControl6: this.question_6,
  questionControl7: this.question_7,
  questionControl8: this.question_8,
  questionControl9: this.question_9,
  questionControl10: this.question_10,
  questionControl11: this.question_11,
  questionControl12: this.question_12,
  questionControl13: this.question_13,
  questionControl14: this.question_14,
  questionControl15: this.question_15,
  questionControl16: this.question_16,
  questionControl17: this.question_17,
  questionControl18: this.question_18,
  questionControl19: this.question_19
});

    //Determine if Update or Create
    if (this._pvqStepUp.data.flow !== 'A' && this._pvqStepUp.data.flow != "B") {
      this.createMode = true;
      this.counter = 0;
    } else {
      this.updateMode = true;
      this.counter = 5;
    }

    this._langToggle.getLanguage().subscribe(
      lang => {
        this.lang = lang.toString();
      });
  }


  checkedState(event, checkBox) {
    if (event.target.checked === true) {
      if (this.counter < this.checkedLimit) {
        this.counter++;
      } else {
        event.target.checked = false;
      }
    } else if (this.counter > 0) {
      let index = event.target.name;
      //When clicking off the checkbox, set value to null
      this.answerControlArr[index].setValue(null);
      this.counter--;
    }
  }

  onSubmit(pvqForm: any) {
    console.log('4176-21 Submitted data --> {}', pvqForm);
    // console.log(pvqForm._value);
    Object.keys(pvqForm._value).map((key) => {
      console.log('4176-21 Object Key: ', key, ' Object Value: ', pvqForm._value[key]);
      // console.log('Question Controm name ' + pvqForm._value[key]);
      if(key.startsWith('questionControl')){
      }
      if(key.startsWith('answerControl')){
        let index = key.replace('answerControl', '');
        console.log('Index is: ' + index);
        console.log('Value is: ', pvqForm._value[key]);
      }
    })
  }

  cancel() {
    this.dirtyModal.show();
  }

  modalStay() {
    this.dirtyModal.hide();
  }

  modalLeave() {
    //Navigate Away to the previous screen
    // this.router.navigate([this.previousScreen]);
    location.assign(this.previousScreen);
  }

  isDisabled() {
    if (this.counter == 5) {
      //Enable if 5 Checkboxes are selected
      return false;
    } else {
      //Disable if 5 Checkboxes are not selected
      return true;
    }
  }

}

HTML:

<form [formGroup]="pvqForm" (ngSubmit)="onSubmit(pvqForm)" novalidate>
      <div *ngFor="let question of questions | sortBy: 'selected'; let i = index" class="row container-generic">
        <div class="col-md-8">
          <div class="container-input-checkbox">
            <label class="container-flex">
            <input formControlName='questionControl{{i}}' #checkBox class="pvq-create-checkbox" type="checkbox" name="{{i}}" (change)="checkedState($event, checkBox)" [checked]="question.selected"> 
            <div class="pvq-create-label">
              <div *ngIf="lang == 'en'">
                  <p aria-label="English Question">{{ question.EN }}</p>
              </div>
              <div *ngIf="lang == 'fr'">
                  <p aria-label="French Question">{{ question.FR }}</p>
              </div>
            </div>
          </label>
            <label [@hideShow]="checkBox.checked ? 'show' : 'hide'">Answer
            <input minlength=4 formControlName='answerControl{{i}}' type="textbox" name="{{i}}">
             <div *ngIf="!pvqForm.controls['answerControl' + i].valid && pvqForm.controls['answerControl' + i].touched" style="color: red;">
               Error here 
             </div>
          </label>
          </div>
        </div>
      </div>

person Taranjit Kang    schedule 21.07.2017    source источник


Ответы (1)


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

Их можно создать с помощью конструктора форм.

Я также сгруппировал вопросы и ответы вместе.

import { FormBuilder } from '@angular/forms'

...

ngOnInit() {
  this.questionGroups = this.fb.array(this.getQuestions().map(question => this.fb.group(question)));

  this.pvqForm = this.fb.group({
    questions: this.questionGroups
  });

  console.log(this.pvqForm);
}

getQuestions() {
  const questionControlArray = [];
  for (let i = 0; i < 20; i++) {
    questionControlArray.push({ 
      question: ['', Validators.required], 
      answer: ['']
    });
  }
  return questionControlArray;
}

Затем они будут отображаться в представлении примерно так:

<form [formGroup]="pvqForm" (ngSubmit)="submit(pvqForm)">
  <div [formArrayName]="'questions'">
    <div *ngFor="let question of questionGroups.controls; let i = index;" class="form-group">
      <div [formGroupName]="i">
        <label class="center-block">{{'question ' + i}}:
          <input formControlName="question" class="form-control"/> 
        </label>
        <label class="center-block">{{'answer ' + i}}:
          <input formControlName="answer" class="form-control"> 
        </label>
      </div>
    </div>
  </div>

  <button>Submit</button>
</form>
person 0mpurdy    schedule 21.07.2017
comment
Спасибо, но это не то, что я искал. Может быть, я мог бы использовать массивы форм в свою пользу и найти решение. А пока еще проиграл. - person Taranjit Kang; 22.07.2017
comment
Итак, вы пытаетесь связать вопросы с ответами и не можете, потому что порядок меняется, а идентификаторы нет, верно? Не могли бы вы опубликовать изображение того, как выглядит компонент? Я боюсь, что было слишком много отсутствующих зависимостей, чтобы я мог создать компонент самостоятельно. - person 0mpurdy; 22.07.2017
comment
Да, если есть способ сгруппировать вопрос или идентификатор вопроса с ответом, таким образом я могу узнать, какой ответ был заполнен пользователем для соответствующего вопроса. Индекс ngFor — это просто индекс для количества вопросов в массиве, при сортировке идентификатор вопроса не эквивалентен индексу. Я разместил код компонента. Я могу сделать снимки экрана позже, когда буду дома. - person Taranjit Kang; 22.07.2017
comment
Вы можете создать группы форм, которые содержат один ответ и один вопрос, а затем отобразить эти группы форм с помощью *ngFor таким образом, чтобы вопрос и ответ были сгруппированы вместе в ответе. - person 0mpurdy; 22.07.2017
comment
Я думал о чем-то подобном, не уверен, как будет выглядеть отправленная форма. Я должен попробовать, когда я дома. - person Taranjit Kang; 22.07.2017
comment
Хорошо, я обновил свой ответ, чтобы включить, как это сделать. Существует также репозиторий git здесь, где я зафиксировал код, который использовал для тестирования. - person 0mpurdy; 22.07.2017
comment
Эй, я решил это по-своему, спасибо за вашу помощь - теперь у меня новая проблема :) Вместо answerControl{{i}} я изменил его на answerControl{{question.questionId}}, затем я просмотрел элементы управления формами и заменил все answerControl на , и получил идентификатор. Затем взял идентификатор, просмотрел массив вопросов и сопоставил идентификатор, чтобы получить вопрос, связанный с ответом. - person Taranjit Kang; 22.07.2017