web-dev-qa-db-ja.com

Angular 2のカスタム検証ルールに基づいてエラーメッセージを表示するには?

Angular 2でフォームを構築するためにテンプレート駆動型のアプローチを使用しており、テンプレートで使用できるカスタムバリデータを正常に作成しました。

ただし、特定のエラーにバインドされた特定のエラーメッセージを表示する方法が見つかりません。フォームが無効である理由を区別したいと思います。どうすれば達成できますか?

        import { Component } from '@angular/core';

    import { NgForm } from '@angular/forms';

    import { Site } from './../site';

    import { BackendService } from './../backend.service';

    import { email } from './../validators';

    import { CustomValidators } from './../validators';

    @Component({
        templateUrl: 'app/templates/form.component.html',
        styleUrls: ['app/css/form.css'],
        directives: [CustomValidators.Email, CustomValidators.Url, CustomValidators.Goof],
        providers: [BackendService]
    })

    export class FormComponent {
        active = true;
        submitted = false;
        model = new Site();

        onSubmit() {
            this.submitted = true;
            console.log(this.model);
        }

        resetForm() {
            this.model = new Site();
            this.submitted = false;
            this.active = false;
            setTimeout(() => this.active = true, 0);
        }

        get diagnostics() {
            return JSON.stringify(this.model)
        }
    }

import { Directive, forwardRef } from '@angular/core';
import { NG_VALIDATORS, FormControl } from '@angular/forms';
import { BackendService } from './backend.service';

function validateEmailFactory(backend:BackendService) {
    return (c:FormControl) => {
        let EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;

        return EMAIL_REGEXP.test(c.value) ? null : {
            validateEmail: {
                valid: false
            }
        };
    };
}

export module CustomValidators {

    @Directive({
        selector: '[email][ngModel],[email][formControl]',
        providers: [
            {provide: NG_VALIDATORS, useExisting: forwardRef(() => CustomValidators.Email), multi: true}
        ]
    })
    export class Email {
        validator:Function;

        constructor(backend:BackendService) {
            this.validator = validateEmailFactory(backend);
        }

        validate(c:FormControl) {
            return this.validator(c);
        }
    }

    @Directive({
        selector: '[url][ngModel],[url][formControl]',
        providers: [
            {provide: NG_VALIDATORS, useExisting: forwardRef(() => CustomValidators.Url), multi: true}
        ]
    })
    export class Url {
        validator:Function;

        constructor(backend:BackendService) {
            this.validator = validateEmailFactory(backend);
        }

        validate(c:FormControl) {
            var pattern = /(https?:\/\/)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,4}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/;

            return pattern.test(c.value) ? null : {
                validateEmail: {
                    valid: false
                }
            };
        }
    }

    @Directive({
        selector: '[goof][ngModel],[goof][formControl]',
        providers: [
            {provide: NG_VALIDATORS, useExisting: forwardRef(() => CustomValidators.Goof), multi: true}
        ]
    })
    export class Goof {
        validate(c:FormControl) {
            return {
                validateGoof: {
                    valid: false
                }
            };
        }
    }
}

AbstractControl#hasError(...) メソッドをチェックするだけで、コントロールに特定のエラーがあるかどうかを確認できます。 FormGroupFormControlは両方ともAbstractControlsです。 FormControlの場合は、エラー名を引数として渡すだけです。例えば

function regexValidator(control: FormControl): {[key:string]: boolean} {
  if (!control.value.match(/^pee/)) {
    return { 'badName': true };
  }
}

<div *ngIf="!nameCtrl.valid && nameCtrl.hasError('badName')"
     class="error">Name must start with <tt>pee</tt>.
</div>

バリデータメソッドは、キーがエラーの名前である文字列/ブールマップを返す必要があります。これは、hasErrorメソッドで確認する名前です。

FormGroupの場合、FormControlへのパスを追加のパラメーターとして渡すことができます。

<div *ngIf="!form.valid && form.hasError('required', ['name'])"
     class="error">Form name is required.</div>

nameは、単に入力のFormControlの識別子です。

FormControlFormGroupの両方のチェックの例を次に示します。

import { Component } from '@angular/core';
import {
  FormGroup,
  FormBuilder,
  FormControl,
  Validators,
  AbstractControl,
  REACTIVE_FORM_DIRECTIVES
} from '@angular/forms';

function regexValidator(control: FormControl): {[key:string]: boolean} {
  if (!control.value.match(/^pee/)) {
    return { 'badName': true };
  }
}

@Component({
  selector: 'validation-errors-demo',
  template: `
    <div>
      <h2>Differentiate Validation Errors</h2>
      <h4>Type in "peeskillet"</h4>
      <form [formGroup]="form">
        <label for="name">Name: </label>
        <input type="text" [formControl]="nameCtrl"/>
        <div *ngIf="!nameCtrl.valid && nameCtrl.hasError('required')"
             class="error">Name is required.</div>
        <div *ngIf="!nameCtrl.valid && nameCtrl.hasError('badName')"
             class="error">Name must start with <tt>pee</tt>.</div>
        <div *ngIf="!form.valid && form.hasError('required', ['name'])"
             class="error">Form name is required.</div>
      </form>
    </div>
  `,
  styles: [`
    .error {
      border-radius: 3px;
      border: 1px solid #AB4F5B;
      color: #AB4F5B;
      background-color: #F7CBD1;
      margin: 5px;
      padding: 10px;
    }
  `],
  directives: [REACTIVE_FORM_DIRECTIVES],
  providers: [FormBuilder]
})
export class ValidationErrorsDemoComponent {
  form: FormGroup;
  nameCtrl: AbstractControl;

  constructor(formBuilder: FormBuilder) {
    let name = new FormControl('', Validators.compose([
      Validators.required, regexValidator
    ]));
    this.form = formBuilder.group({
      name: name
    });
    this.nameCtrl = this.form.controls['name'];
  }
}

更新

わかりましたので、私はそれを動作させましたが、それは少し冗長です。入力の個々のFormControlに適切にアクセスする方法を理解できませんでした。だから私はただFormGroupへの参照を作成しました

<form #f="ngForm" novalidate>

次に、有効性を確認するために、フォームコントロール名のパスを渡したhasErrorオーバーロードを使用します。ために <input>namengModelを使用する場合、nameの値は、その名前をFormGroup名としてメインFormControlに追加されます。だからあなたはそれにアクセスすることができます

`f.form.hasError('require', ['nameCtrl'])`

name=nameCtrlf.formfは、NgFormメンバー変数FormGroupを持つformインスタンスです。

以下はリファクタリングされた例です

import { Component, Directive } from '@angular/core';
import {
  FormControl,
  Validators,
  AbstractControl,
  NG_VALIDATORS,
  REACTIVE_FORM_DIRECTIVES
} from '@angular/forms';

function validateRegex(control: FormControl): {[key:string]: boolean} {
  if (!control.value || !control.value.match(/^pee/)) {
    return { 'badName': true };
  }
}
@Directive({
  selector: '[validateRegex]',
  providers: [
    { provide: NG_VALIDATORS, useValue: validateRegex, multi: true }
  ]
})
export class RegexValidator {
}

@Component({
  moduleId: module.id,
  selector: 'validation-errors-template-demo',
  template: `
    <div>
      <h2>Differentiate Validation Errors</h2>
      <h4>Type in "peeskillet"</h4>
      <form #f="ngForm" novalidate>
        <label for="name">Name: </label>

        <input type="text" name="nameCtrl" ngModel validateRegex required />

        <div *ngIf="!f.form.valid && f.form.hasError('badName', ['nameCtrl'])"
             class="error">Name must start with <tt>pee</tt>.</div>

        <div *ngIf="!f.form.valid && f.form.hasError('required', ['nameCtrl'])"
             class="error">Name is required.</div>
      </form>
    </div>
  `,
  styles: [`
    .error {
      border-radius: 3px;
      border: 1px solid #AB4F5B;
      color: #AB4F5B;
      background-color: #F7CBD1;
      margin: 5px;
      padding: 10px;
    }
  `],
  directives: [REACTIVE_FORM_DIRECTIVES, RegexValidator]
})
export class ValidationErrorsTemplateDemoComponent {

}
14
Paul Samsotha

Angularでこの問題に取り組むために、AngularJsのng-messagesに似た一連のディレクティブを作成しました。 https://github.com/DmitryEfimenko/ngx-messages

<div [val-messages]="myForm.get('email')">
  <span val-message="required">Please provide email address</span>
  <span val-message="server" useErrorValue="true"></span>
</div>
2
Dmitry