Form Validation Error Message Not Showing in react

2019-08-17 17:45发布

问题:

Why is this form control error messages not showing I have an array and I'm reading it and generate dynamically questions. I can't find what is the mistake I have done in this code.

I have to show 4 error messages

  1. Required
  2. Min value validate
  3. Max value validate
  4. Unique value validate

I have an array which contains a question

questions: any = [{
      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,
      answers: ['Location', 'Confort', 'Service', 'Value for money']
    }];

I generated form controller dynamically likewise,

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

private buildSubGroup(question) {
    switch (question.qType) {
       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: [this.uniqueNumbersValidator()] }
        );
        default:
        throw new Error('unhandled question type');
    }
  }

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

Here is my html code

<div class="form-group"  formGroupName="{{'q' + question.qNo}}">
                                                <label class="control-label"> {{question.qNo}})
                                                    {{question.question}}</label>
                                                <div class="ml-3">
                                                    <table>
                                                        <tr *ngFor="let anwr of question.answers; let a=index">
                                                            <td>{{a+1}}. {{anwr}} </td>
                                                            <div class="invalid-feedback"
                                                            *ngIf="surveyQuestionForm.get('q'+ question.qNo).touched && surveyQuestionForm.get('q'+ question.qNo).hasError('required')">
                                                            Answer required</div>
                                                        <div class="invalid-feedback"
                                                            *ngIf="surveyQuestionForm.get('q'+ question.qNo).touched && surveyQuestionForm.get('q'+ question.qNo).hasError('max')">
                                                            max value</div>
                                                        <div class="invalid-feedback"
                                                            *ngIf="surveyQuestionForm.get('q'+ question.qNo).touched && surveyQuestionForm.get('q'+ question.qNo).hasError('min')">
                                                            min value</div>
                                                        <div class="invalid-feedback"
                                                            *ngIf="surveyQuestionForm.get('q'+ question.qNo).touched && surveyQuestionForm.get('q'+ question.qNo).hasError('notAllUnique')">
                                                            Already inserted value</div>
                                                            <td>
                                                                <input type="number" style="width:40px;"
                                                                    id="q{{question.qNo}}_{{a}}"
                                                                    [ngClass]="{'is-invalid': surveyQuestionForm.get('q'+ question.qNo).errors 
                                                                    && surveyQuestionForm.get('q'+ question.qNo).touched}"
                                                                    formControlName="{{anwr}}"
                                                                    class="text-center" />
                                                            </td>
                                                        </tr>

                                                    </table>
                                                </div>
                                            </div>

Here is stackblitz code https://stackblitz.com/edit/angular-pxdesk

please tell me what is the problem having this.

thanks

回答1:

the problem here is you're not differentiating between the group having an error or the control having an error.

you need to make sure you're accessing the error on the control when the validator is o the control, and on the group when it's on the group, so you need to be doing this:

<div class="invalid-feedback"
    *ngIf="surveyQuestionForm.get('q'+ question.qNo).get(anwr).touched && surveyQuestionForm.get('q'+ question.qNo).get(anwr).hasError('required')">
    Answer required</div>
<div class="invalid-feedback"
    *ngIf="surveyQuestionForm.get('q'+ question.qNo).get(anwr).touched && surveyQuestionForm.get('q'+ question.qNo).get(anwr).hasError('max')">
    max value</div>
<div class="invalid-feedback"
    *ngIf="surveyQuestionForm.get('q'+ question.qNo).get(anwr).touched && surveyQuestionForm.get('q'+ question.qNo).get(anwr).hasError('min')">
    min value</div>

for the control level errors. If you don't make sure to be checking if the control has been touched as well, then the errors will appear at the wrong time.

for the group level error, you probably want to move it outside of the individual controls, and display it with the group, since it is a group level error. In your current scheme, if you had a duplicate value, the error would appear next to every touched control. so do this instead...

<div class="invalid-feedback"
        *ngIf="surveyQuestionForm.get('q'+ question.qNo).touched && surveyQuestionForm.get('q'+ question.qNo).hasError('notAllUnique')">
        Already inserted value</div>

outside of your ngFor over the answers. notice here, we're accessing the group since this is a group level error.

side note: you probably want your max validator to be 4 and not 3, since you want 4 different numbers.

here is the fixed blitz: https://stackblitz.com/edit/angular-hbtvnc?file=src/app/app.component.html