如何使用响应式表单在 angular 2+ 中动态创建表单控制器

How to create form controllers dynamically in angular 2+ using Reactive forms

我有一个数组 questions.I 必须阅读它并显示问题 dynamically.So 我有几个与之相关的问题。

1) 我不知道如何将控制器初始化为 FormBuilder 实例。

2) 如何动态创建问题

3) 如何动态添加验证

我使用 angular 8.

创建了这个项目

我在一个调查中主要有4种问题。

  1. 多选题(只需select一个答案)

  2. 多个Select(用户可以select多个答案)

  3. 排名问题(用户必须给出正确的答案顺序)

  4. 描述性的(用户自己回答即可)

这是我的问题数组

questions: any = [
{
  id: 11,
  surveyNo: 5,
  qNo: 1,
  question: 'What is the country you would like to travel?',
  qType: 1,
  noAnswrs: 4,
  answerType: 1,
  answrs: ['America', 'Australia', 'India', 'England']
},
{
  id: 12,
  surveyNo: 5,
  qNo: 2,
  question: 'What type of credit cards do you have?',
  qType: 2,
  noAnswrs: 4,
  answerType: 1,
  answrs: ['Visa', 'Mastercard', 'American Express', 'Discover']
},
{
  id: 13,
  surveyNo: 5,
  qNo: 3,
  question: 'Please rank the following features in order of importance,where 1 is the most important to you.?',
  qType: 3,
  noAnswrs: 4,
  answerType: 1,
  answrs: ['Location', 'Confort', 'Service', 'Value for money']
},
{
  id: 14,
  surveyNo: 5,
  qNo: 4,
  question: 'What is your idea about our institute?',
  qType: 4,
  noAnswrs: 0,
  answerType: 1,
  answrs: []
}];

这里是html代码

<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

<div class="container">
    <div class="row">
        <div class="col-md-12">
            <div class="card">
                <div class="card-header  bg-transparent border-success">
                    <h3>15 questions</h3>
                </div>
                <div class="card-body">
                    <div class="row">
                        <div class="col-md-12">
                            <form [formGroup]="surveyQuestionForm">
                                <div class="form-group">
                                    <label class="control-label"> 1) What is the country you would like to
                                        travel?</label>
                                    <div class="ml-3">
                                        <table>
                                            <th width="auto"></th>
                                            <th width="auto"></th>
                                            <tr>
                                                <td>1. America</td>
                                                <td>
                                                    <div class=" custom-radio custom-control">
                                                        <input type="radio" class="custom-control-input" id="q1_1"
                                                            name="q1" value="1" formControlName="q1" />
                                                        <label class="custom-control-label" for="q1_1">

                                                        </label>
                                                    </div>
                                                </td>
                                            </tr>
                                            <tr>
                                                <td>2. Australia </td>
                                                <td>
                                                    <div class=" custom-radio custom-control">
                                                        <input type="radio" class="custom-control-input" id="q1_2"
                                                            name="q1" value="2" formControlName="q1" />
                                                        <label class="custom-control-label" for="q1_2"></label>
                                                    </div>
                                                </td>
                                            </tr>
                                            <tr>
                                                <td>3. India </td>
                                                <td>
                                                    <div class="custom-radio custom-control">
                                                        <input type="radio" class="custom-control-input" id="q1_3"
                                                            name="q1" value="3" formControlName="q1" />
                                                        <label class="custom-control-label" for="q1_3"></label>
                                                    </div>
                                                </td>
                                            </tr>
                                            <tr>
                                                <td>4. England </td>
                                                <td>
                                                    <div class=" custom-control  custom-radio">
                                                        <input type="radio" class="custom-control-input" id="q1_4"
                                                            name="q1" value="4" formControlName="q1" />
                                                        <label class="custom-control-label" for="q1_4"></label>
                                                    </div>
                                                </td>
                                            </tr>
                                        </table>





                                    </div>
                                </div>
                               
                                <div class="form-group">
                                    <label class="control-label"> 2) What type of credit cards do you have?</label>
                                    <div class="ml-3">
                                        <table>
                                            <th width="auto"></th>
                                            <th width="auto"></th>
                                            <tr>
                                                <td>1. Visa </td>
                                                <td>
                                                    <div class="custom-control custom-checkbox">
                                                        <input type="checkbox" class="custom-control-input" id="q2_1"
                                                            value="1" formControlName="q2" />
                                                        <label class="custom-control-label" for="q2_1"></label>
                                                    </div>
                                                </td>
                                            </tr>
                                            <tr>
                                                <td>2. Mastercard</td>
                                                <td>
                                                    <div class="custom-control custom-checkbox">
                                                        <input type="checkbox" class="custom-control-input" id="q2_2"
                                                            value="2" formControlName="q2" />
                                                        <label class="custom-control-label" for="q2_2"></label>
                                                    </div>
                                                </td>
                                            </tr>
                                            <tr>
                                                <td>3. American Express</td>
                                                <td>
                                                    <div class="custom-control custom-checkbox">
                                                        <input type="checkbox" class="custom-control-input" id="q2_3"
                                                            value="3" formControlName="q2" />
                                                        <label class="custom-control-label" for="q2_3"></label>
                                                    </div>
                                                </td>
                                            </tr>
                                            <tr>
                                                <td>4. Discover</td>
                                                <td>
                                                    <div class="custom-control custom-checkbox">
                                                        <input type="checkbox" class="custom-control-input" id="q2_4"
                                                            value="4" formControlName="q2" />
                                                        <label class="custom-control-label" for="q2_4"></label>
                                                    </div>
                                                </td>
                                            </tr>
                                        </table>
                                    </div>
                                </div>
                                <div class="form-group">
                                    <label class="control-label"> 3) Please rank the following features in order of importance,where 1 is the most important to you.?</label>
                                    <div class="ml-3">
                                        <table>
                                             
                                            <tr>
                                                <td>1. Location </td>
                                                <div class="invalid-feedback"
                                                 *ngIf="surveyQuestionForm.get('q3').touched && surveyQuestionForm.get('q3').hasError('required')">Answer required</div>
                                                 <div class="invalid-feedback"
                                                 *ngIf="surveyQuestionForm.get('q3').touched && surveyQuestionForm.get('q3').hasError('max')">max value</div>
               
                                                <td><input type="number" style="width:40px;" id="q3_1"  
                                                    [ngClass]="{'is-invalid': surveyQuestionForm.get('q3').errors && surveyQuestionForm.get('q3').touched}"
                                                        formControlName="q3" class="text-center" /></td>
                                            </tr>
                                              <tr>
                                                <td>2. Confort </td>
                                                <td><input type="number" style="width:40px;" id="q3_1"  
                                                         class="text-center" /></td>
                                            </tr>
                                            <tr>
                                                <td>3. Service </td>
                                                <td><input type="number" style="width:40px;" id="q3_1"  
                                                          class="text-center" /></td>
                                            </tr>
                                            <tr>
                                                <td>4. Value for money </td>
                                                <td><input type="number" style="width:40px;" id="q3_1"  
                                                          class="text-center" /></td>
                                            </tr>
                                        </table>


                                    </div>
                                </div>
                                <div class="form-group">
                                    <label class="control-label"> 4) What is your idea about our institute?</label>
                                    <div class="ml-3">
                                        <table>
                                            <th width="auto"></th>
                                            <th></th>

                                            <tr>

                                                <td><textarea class="form-control" rows="5" id="comment" name="text"
                                                        formControlName="q4"></textarea></td>
                                            </tr>

                                        </table>


                                    </div>
                                </div>
                                
                            </form>
                        </div>
                    </div>
                    <div class="row">
                        <div class="col-md-12">
                            <button class="btn btn-primary">Submit</button>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

这是打字稿代码

 surveyQuestionForm: FormGroup;
  constructor(private fb: FormBuilder) { }
  questions: any = [
    {
      id: 11,
      surveyNo: 5,
      qNo: 1,
      question: 'What is the country you would like to travel?',
      qType: 1,
      noAnswrs: 4,
      answerType: 1,
      answrs: ['America', 'Australia', 'India', 'England']
    },
    {
      id: 12,
      surveyNo: 5,
      qNo: 2,
      question: 'What type of credit cards do you have?',
      qType: 2,
      noAnswrs: 4,
      answerType: 1,
      answrs: ['Visa', 'Mastercard', 'American Express', 'Discover']
    },
    {
      id: 13,
      surveyNo: 5,
      qNo: 3,
      question: 'Please rank the following features in order of importance,where 1 is the most important to you.?',
      qType: 3,
      noAnswrs: 4,
      answerType: 1,
      answrs: ['Location', 'Confort', 'Service', 'Value for money']
    },
    {
      id: 14,
      surveyNo: 5,
      qNo: 4,
      question: 'What is your idea about our institute?',
      qType: 4,
      noAnswrs: 0,
      answerType: 1,
      answrs: []
    }
  ];
  ngOnInit() {
    this.createForms();
  }
  createForms(): any {
    this.surveyQuestionForm = this.fb.group({
      q1: ['', [Validators.required]],
      q2: ['', [Validators.required]],
      q3: ['', [Validators.required, Validators.min(1), Validators.max(3)]],
      q4: ['', [Validators.required]]
     });
  }

这里是试题预览

这是验证器必须使用的

  1. 需要验证器
  2. 最小值(1) 和最大值(4)
  3. 必须检查是否插入了已插入的值(不知道如何动态执行此操作)

我刷了很多文章,但那些都不是动态创建的。 请帮我做这个

谢谢

不太清楚你在追求什么,但我认为它看起来像这样....

private buildSubGroup(question) {
  switch (question.qType) {
    case 2:
      return this.fb.group(
        question.answers.reduce((subGroup, answer) => {
          return Object.assign(subGroup, {[answer]: [false]});
        }, {}), {validators: [atLeastOneRequired()]} // validation rules here unclear? is at least 1 required?
      );
    case 3:
      return this.fb.group(
        question.answers.reduce((subGroup, answer) => {
          return Object.assign(subGroup, {[answer]: ['', [Validators.required, Validators.min(1), Validators.max(3)]]});
        }, {}), {validators: [uniqueNumbersValidator()]}
      );
    case 1: // it's counter intuitive but these are actually the same structure due to how angular handles radio input
    case 4:
      return this.fb.group({answer: ['', [Validators.required]]});
    default:
      throw new Error('unhandled question type');
  }
}

this.surveyQuestionForm = this.fb.group(
  this.questions.reduce((group, question) => {
    return Object.assign(group, {['q' + question.qNo]: this.buildSubGroup(question)});
  }, {});
);

所以基本上,你把你的问题数组减少到一个对象,用 'q' 加上 qNo 作为键,值是依赖于 qType 的子表单组......你还需要一些需要编写组级自定义验证器以确认每个数字只出现一次并且至少选择了一个,它可能如下所示:

   function atLeastOneRequired() {
     return (ctrl: AbstractControl) => {
       let fg = ctrl as FormGroup;
       let atLeastOneTrue = Object.values(fg.controls).some(fc => !!fc.value);
       return (atLeastOneTrue ) ? null : {atLeastOneRequired: true};
     };
   }

   function uniqueNumbersValidator() {
     return (ctrl: AbstractControl) => {
       let fg = ctrl as FormGroup;
       let allUnique = true;
       let values = [];
       Object.values(fg.controls).forEach(fc => {
         let val = fc.value;
         if (val && allUnique) {
           if (values.includes(val)) {
             allUnique = false;
             break;
           }
           values.push(val);
         }
       });
       return (allUnique) ? null : {notAllUnique: true};
     }
   }

模板绑定本身与您拥有它的方式有很大不同,但这回答了如何以动态方式构建表单控件。您可以通过迭代问题数组并适当地绑定 formGroupName 和 formControlName 指令以及 ngSwitch 指令来构建问题模板,其方式与我在此处使用 switch 语句构建表单组的方式类似。