web-dev-qa-db-ja.com

Angular 2-リアクティブフォーム検証メッセージ

私の目的は、すべての検証メッセージをhtmlファイルではなくコンポーネントに入れることです

サインアップページがあり、以下のフィールドがあります。

public buildRegisterForm() {
this.userForm = this.fb.group({
  firstName: ['', [Validators.required, Validators.minLength(3)]],
  lastName: ['', [Validators.required, Validators.maxLength(50)]],
  emailGroup: this.fb.group({
    email: ['', [Validators.required, Validators.pattern(this.emailPattern)]],
    retypeEmail: ['', Validators.required],
  }, { validator: formMatcherValidator('email', 'retypeEmail') }),
  passwordGroup: this.fb.group({
    password: ['', [Validators.required, strongPasswordValidator()]],
    retypePassword: ['', Validators.required],
  }, { validator: formMatcherValidator('password', 'retypePassword')}),
});
}

私はこのチュートリアルに従っています link すべての検証メッセージをhtmlファイルではなくコンポーネントファイルに入れたいと思うことを達成します。

export const validationMessages = {
'firstName': {
'required': 'Your first name is required.',
'minlength': 'Your first name must be at least 3 characters long.'
},
'lastName': {
'required': 'Your last name is required.',
'minlength': 'Your last name must be less than 50 characters long.'
},
'emailGroup': {
  'email': {
      'required': 'Your email is required',
      'pattern': 'Your login email does not seem to be a valid email address.'
     },
 'retypeEmail': {
      'required': 'Your retype email is required',
      'match': 'The email provided do not match.'
   },
},
'passwordGroup':{
     'password': {
         'required': 'Your password is required',
         'strongPassword': 'Your password must be between 8 - 15 characters and must contain at least three of the following: upper case letter, lower case letter, number, symbol.'
     },
   'retypePassword': {
       'required': 'Your retype password is required',
        'match': 'The password provided do not match.'
  }
}


onValueChangedメソッド

private onValueChanged(data?: any) {
if (!this.userForm) { return; }
const form = this.userForm;

// tslint:disable-next-line:forin
for (const field in this.formErrors) {
  // clear previous error message (if any)
  this.formErrors[field] = '';
  let control = form.get(field);
  // console.log("control", control.dirty);

  console.log("controlEmail", control);
  if (control && (control.dirty || control.touched) && control.invalid) {
    let messages = validationMessages[field];
    // tslint:disable-next-line:forin
    for (const key in control.errors) {
      this.formErrors[field] += messages[key] + ' ';
    }
  }
 }
}

また、multiformBuider Groupまたはネストされたオブジェクトがある場合、このメソッドは機能しません。この1のヒントはありますか?
これと同様 ネストされたフォームグループでリアクティブフォームを検証する方法?

10
weikian

私の見方では、onValueChanged(data)- method内にネストされたループを作成する必要があります。ネストされたグループが非常に多いため、これを複製しません。ただし、ネストされたループは汎用なので、すべてのグループで機能します。ただし、ここでは、複数ではなく1つのネストされたグループのみの例を示します。私はヒーローの例を使用しています。

ネストされたグループ名はgroupであり、その中のformcontrolはchildと呼ばれます。

したがって、コードで使用されるformErrorsは、内部のchildgroupを含める必要があります。

formErrors = {
  'name': '',
  'power': '',
  'group':{
    'child': ''
  }
};

したがって、テンプレートに検証を追加するときは、次を使用する必要があります。

<div *ngIf="formErrors.group.child">
   {{ formErrors.group.child }}
</div>

検証メッセージはgroupの中にありませんが、他の検証メッセージと同じです:

validationMessages = {
  'name': {
    'required': 'Name is required.',
  },
  'power': {
    'required': 'Power is required.'
  },
  'child': {
    'required': 'Child is required.',
  }
};

最後に、変更されたonValueChanges

onValueChanged(data?: any) {
  if (!this.heroForm) { return; }
  const form = this.heroForm;

  // iterate toplevel of formErrors
  for (const field in this.formErrors) {
    // check if the field corresponds a formgroup (controls is present)
    if(form.get(field).controls ) {
      // if yes, iterate the inner formfields
      for(const subfield in form.get(field).controls) {
        // in this example corresponds = "child", reset the error messages
        this.formErrors[field][subfield] = '';
        // now access the actual formfield
        const control = form.get(field).controls[subfield];
        // validate and show appropriate error message
        if (control && control.dirty && !control.valid) {
          const messages = this.validationMessages[subfield];
          for (const key in control.errors) {
            this.formErrors[field][subfield] += messages[key] + ' ';
          }
        }
      }
    } 
    // does not contain a nested formgroup, so just iterate like before making changes to this method
    else {
      const control = form.get(field);
      this.formErrors[field] = '';
      if (control && control.dirty && !control.valid) {
        const messages = this.validationMessages[field];
        for (const key in control.errors) {
          this.formErrors[field] += messages[key] + ' ';
        }
      } 
    }
  }
}

最後に、デモ:)

Plunker

ただし、これが機能することを覚えておく必要がありますが、ネストされたグループ内にネストされたグループがある場合、これは機能しないため、onValueChangesでさらに別のループを作成する必要がありますここにその問題があります;)

8
AJT82

また、元のonValueChangedメソッドと共に次を使用できます。

formErrors = {
  'name': '',
  'power': '',
  'group.child':''
};

validationMessages = {
  'name': {
    'required': 'Name is required.',
  },
  'power': {
    'required': 'Power is required.'
  },
  'group.child': {
    'required': 'Child is required.',
  }
};

onValueChanged(data?: any) {
    if (!this.heroForm) { return; }
    const form = this.heroForm;

    for (const field in this.formErrors) {
      // clear previous error message (if any)
      this.formErrors[field] = '';
      const control = form.get(field);

      if (control && control.dirty && !control.valid) {
        const messages = this.validationMessages[field];
        for (const key in control.errors) {
          this.formErrors[field] += messages[key] + ' ';
        }
      }
    }
  }
3
me98278

Nehalのソリューションが機能することを願っていますが、それを実現できませんでした。 @ AJT_82コードを少し操作した後、私のソリューションを見つけました。私の必要性は、グループとしてパスワードを確認したいことから生まれました。彼のソリューションはそれをカバーしませんでした。だから、私は私のために働いた完全なソリューションを作成するために使用した他の部分を含めました。

この問題に遭遇して、2で使用していた方法が同じように機能しなくなったことを発見した後、angular 4でパスワード確認の検証を試みました。多くの情報がドキュメントのさまざまな領域で断片化されているため、これに役立ちます。

したがって、明確にするために、これはAngularのリアクティブフォームメソッドに従います。他の検証制限もあるインスタンスでパスワードを検証するためにこれを書きました(パスワードは4〜24文字である必要があります、など)。この方法は、いくつかの小さな調整を行って確認メールを送信する場合にも同様に機能するはずです。

最初に、グループのバリデーターを比較するには、htmlフォームにformGroupName = ""識別子を使用して識別されるサブグループが必要です。これは、プライマリ[formGroup]識別子内にあります。比較される入力は、この内部にある必要がありますformGroupNameラベル付き要素。私の場合、それは単なるdivです。

<div class="title">Hero Registration Form</div>
<form [formGroup]="regForm" id="regForm" (ngSubmit)="onSubmit()">
    <div class="input">
        <label for="heroname">Heroname</label> <input type="text"
            id="heroname" class="form-control" formControlName="heroname"
            required />
        <div *ngIf="formErrors.heroname" class="hero-reg-alert">{{
            formErrors.heroname }}</div>
    </div>
    <div class="input">
        <label for="email">Email</label> <input type="email" id="email"
            class="form-control" formControlName="email" required />
        <div *ngIf="formErrors.email" class="hero-reg-alert">{{
            formErrors.email }}</div>
    </div>
    <div formGroupName="password">
        <div class="input">
            <label for="password1">Password</label> <input type="password"
                id="password1" class="form-control" formControlName="password1"
                required />
            <div *ngIf="formErrors.password.password1" class="hero-reg-alert">
                {{ formErrors.password.password1 }}</div>
        </div>
        <div class="input">
            <label for="password2">Re-Enter Password</label> <input
                type="password" id="password2" class="form-control"
                formControlName="password2" required />
            <div
                *ngIf="formErrors.password.password2 || formErrors.password.password"
                class="hero-reg-alert">{{ formErrors.password.password }} {{
                formErrors.password.password2 }}</div>
        </div>
    </div>
    <button type="submit" [disabled]="!regForm.valid">
        <span id="right-btntxt">SUBMIT</span>
    </button>
</form>

あなたは私がformErrorsのサブ値を持っていることに気付くかもしれません

formErrors.password.password
formErrors.password.password1
formErrors.password.password2

これらは私のコードに組み込まれています...

  formErrors = {
    'username': '',
    'email': '',
    'password': {
      'password': '',
      'password1': '',
      'password2': ''
    }
  };

「password1」と「password2」は各フィールドのformErrorsを保持し、「password」は不一致(入力した2つのパスワードが等しくない)の場合にエラーを保持するため、このように作成しました。

OnValueChanged()式があります。フィールドがFormGroupのインスタンスかどうかを確認します。その場合、最初にそのフィールドグループのカスタム検証をチェックし( 'this.formErrors [field] [field]'に保存)、次にサブフィールド検証の処理に進みます。 FieldGroupのインスタンスではない場合、angular.ioのガイドドキュメントの例に従って検証が処理されます。

  onValueChanged(data?: any) {
    if (!this.regForm) {return;}
    const form = this.regForm;

    for (const field in this.formErrors) {
      const formControl = form.get(field);
      if (formControl instanceof FormGroup) {
        this.formErrors[field][field] = '';
        // check for custom validation on field group
        const control = form.get(field);
        // handle validation for field group
        if (control && control.dirty && !control.valid) {
          const messages = this.validationMessages[field];
          for (const key in control.errors) {
            this.formErrors[field][field] += messages[key] + ' ';
          }
        }
        // handle validation for subfields
        for (const subfield in formControl.controls) {
          console.log('SUBFIELD', subfield);
          this.formErrors[field][subfield] = '';
          const control = formControl.controls[subfield];
          if (control && control.dirty && !control.valid) {
            const messages = this.validationMessages[subfield];
            for (const key in control.errors) {
              this.formErrors[field][subfield] += messages[key] + ' ';
            }
          }
        }
      } 
        // alternate validation handling for fields without subfields (AKA not in a group)
      else {
        const control = form.get(field);
        this.formErrors[field] = '';
        if (control && control.dirty && !control.valid) {
          const messages = this.validationMessages[field];
          for (const key in control.errors) {
            this.formErrors[field] += messages[key] + ' ';
          }
        }
      }
    }
  }

FieldGroupに独自の名前のサブフィールドを与えることにより、FieldGroupに検証を保存できます。通常のonValueChangeコードでこれを行おうとすると、行のサブフィールドが上書きされます...

    this.formErrors[field] = '';

FieldGroup検証を保存する場所を提供しないと、サブフィールドが上書きされるか、FieldGroupが処理されません。

必要な場合、これがbuildFomr()を使用してフォームを構築する方法です。

 buildForm(): void {
    this.regForm = this.formBuilder.group({
      'username': [this.registerUser.username, [
        Validators.required,
        Validators.minLength(4),
        Validators.maxLength(24)
      ]
      ],
      'email': [this.registerUser.email, [
        Validators.email,
        Validators.required
      ]
      ],
      'password': this.formBuilder.group({
        'password1': [this.registerUser.password, [
          Validators.required,
          Validators.minLength(8),
          Validators.maxLength(24)
        ]
        ],
        'password2': ['', [
          Validators.required
        ]
        ]
      }, {validator: this.passwordMatchValidator})
    }); // end buildForm

    this.regForm.valueChanges
      .subscribe(data => this.onValueChanged(data));

    this.onValueChanged(); // (re)set validation messages now
  }

これはカスタム検証関数です...

  passwordMatchValidator(control: FormGroup): {[key: string]: any} {
    return control.get('password1').value !== control.get('password2').value ? {mismatch: true} : null;
  }
1
Jonathan Borgia