はじめに
こちらの記事 でコンポーネントでの検証機能の実現について触れたが、ディレクティブでも検証機能を実現できる。
本記事ではその方法についてみていく。
更新情報
2021/01/09
- 記事内で扱ったコードを Angular
v11.0.5
で確認しました
作業環境
環境 | バージョン | 備考 |
---|---|---|
Angular CLI |
|
$ ng --version |
Angular |
|
同上 |
TypeScript | v4.0.2 | 同上 |
Node.js |
|
$ node --version |
npm |
|
$ npm --version |
ng version の結果
$ ng version
_ _ ____ _ ___
/ \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _|
/ △ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | |
/ ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | |
/_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___|
|___/
Angular CLI: 11.0.5
Node: 12.18.3
OS: darwin x64
Angular: 11.0.5
... animations, cli, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, router
Ivy Workspace: Yes
Package Version
---------------------------------------------------------
@angular-devkit/architect 0.1100.5
@angular-devkit/build-angular 0.1100.5
@angular-devkit/core 11.0.5
@angular-devkit/schematics 11.0.5
@schematics/angular 11.0.5
@schematics/update 0.1100.5
rxjs 6.6.0
typescript 4.0.2
関連記事
本記事で扱う構成
src/
`-app/
`-component/
| `-use-directive/ # ディレクティブを適用するコンポーネント
| `- use-directive.component.css
| `- use-directive.component.html
| `- use-directive.component.ts
`-directive/
| `- network-address-validator.directive.ts # 本記事で扱うディレクティブ
`- app.component.css
`- app.component.html
`- app.component.ts
`- app.module.ts
属性ディレクティブで validation を実装
import { Directive, Input } from '@angular/core';
import { AbstractControl, NG_VALIDATORS, Validator } from '@angular/forms';
@Directive({
selector: '[appNetworkAddressValidator][ngModel]',
providers: [
{provide: NG_VALIDATORS, useExisting: NetworkAddressValidatorDirective, multi: true}
]
})
export class NetworkAddressValidatorDirective implements Validator {
/**
* 入力された内容の最小文字数チェック
* アドレスの最小文字数である x.x.x.x の 7 を定義
*
* @type {number}
* @memberof ValidationComponent
*/
public readonly minLength: number = 7;
/**
* 入力された内容の最大文字数チェック
* アドレスの最大文字数である xxx.xxx.xxx.xxx の 15 を定義
*
* @type {number}
* @memberof ValidationComponent
*/
public readonly maxLength: number = 15;
/**
* 入力された内容のパターンチェック
* 正規表現で x.x.x.x ~ xxx.xxx.xxx.xxx のパターンマッチングを実現させる
*
* @type {string}
* @memberof ValidationComponent
*/
public readonly pattern: string = '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])[¥.]){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$';
/**
* 入力値に対する validation を実施する
*
* @param {AbstractControl} control 検証対象の form 要素
* @returns {{[key: string]: any}} 検証結果
* @memberof ValidationDirective
*/
validate(control: AbstractControl): {[key: string]: any} {
const address: string = control.value;
if (!address) {
return { required: true };
}
if (address.length < this.minLength) {
return { minLength: true };
}
if (address.length > this.maxLength) {
return { maxLength: true };
}
const patternRegex: any = new RegExp(this.pattern);
if (!patternRegex.test(address)) {
return { pattern: true };
}
return {};
}
}
ポイント大きく分けて 3つ。
- validation に必要なモジュールの import と implements
- @Directive デコレータでディレクティブ情報を定義する
- validation の実装
以下、上から順にみていく。
validation に必要なモジュールの import と implements
import { AbstractControl, NG_VALIDATORS, Validator } from '@angular/forms';
// 中略
export class NetworkAddressValidatorDirective implements Validator {
// 省略
}
import
している各モジュールに関する簡単な説明は以下のとおり。
- AbstractControl
- 検証対象のコントロールが継承している抽象クラスで
- 後述の
validate
メソッドで受け取る引数の型として使用している
- NG_VALIDATORS
- 標準の検証ルール
- 独自の検証ルールを儲けたい場合は後述の
@Directive
デコレータで行なっているように、これに検証クラスを追加する
- Validator
- 検証ルールを定義するためのインターフェイス
- これを implements することで後述の
validate
メソッドで検証ルールを実装できる
@Directive デコレータでディレクティブ情報を定義する
@Directive({
selector: '[appNetworkAddressValidator][ngModel]',
providers: [
{provide: NG_VALIDATORS, useExisting: NetworkAddressValidatorDirective, multi: true}
]
})
@Directive
デコレータで検証機能に関する情報を定義する。
各パラメータに関する説明は次のとおり。
- selector
-
appNetworkAddressValidator
はコンポーネントのテンプレートで指定する文字列を定義する - ポイントは次の ```ngModel`` で、これが指定されていないと検証機能が動作しない
-
- providers
- ここで設定している意味は「標準の検証ルールである
NG_VALIDATORS
にNetworkAddressValidatorDirective
で定義して 検証ルールを追加 する」となる -
multi
パラメータにtrue
がセットされている点に注目で、これが定義されていないと「標準の検証ルールを上書き」する動きとなってしまう
- ここで設定している意味は「標準の検証ルールである
validation の実装
/**
* 入力値に対する validation を実施する
*
* @param {AbstractControl} control 検証対象の form 要素
* @returns {{[key: string]: any}} 検証結果
* @memberof ValidationDirective
*/
validate(control: AbstractControl): {[key: string]: any} {
const address: string = control.value;
if (!address) {
return { required: true };
}
if (address.length < this.minLength) {
return { minLength: true };
}
if (address.length > this.maxLength) {
return { maxLength: true };
}
const patternRegex: any = new RegExp(this.pattern);
if (!patternRegex.test(address)) {
return { pattern: true };
}
return {};
}
検証機能の実装部分。処理の流れは次のとおり。
- 引数:
control: AbstractControl
- 本ディレクティブを適用しているテンプレートのフォーム要素を受け取る
- 検証処理
- 引数で受け取った
control
からcontrol.value
で値を取り出して検証処理を実行する - この例では「未入力チェック」「最小文字列チェック」「最長文字列チェック」「正規表現によるパターンチェック」を行っている
- エラー時はここで返却したパラメータを利用しているテンプレートでチェックすることで、エラー内容に応じたメッセージを出力することになる
- 引数で受け取った
コンポーネントからディレクティブを利用する
コンポーネントからディレクティブの検証機能を利用するコードは次のようになる。
テンプレート
<form #formCheck="ngForm" class="formCheck">
<div class="input_ip_address">
<label>IPアドレス:</label>
<input
type="text"
class="input_text"
name="input_ip_address"
[(ngModel)]="inputAddress"
#validateAddress="ngModel"
required
appNetworkAddressValidator
>
<div class="caution">
<div *ngIf="validateAddress.errors && (validateAddress.dirty || validateAddress.touched)">
<div [hidden]="!validateAddress.errors.required">
※ 項目が未入力です
</div>
<div [hidden]="!validateAddress.errors.minLength">
※ 入力した内容が短すぎます
</div>
<div [hidden]="!validateAddress.errors.maxLength">
※ 入力した内容が長すぎます
</div>
<div [hidden]="!validateAddress.errors.pattern">
※ 入力した内容に誤りがあります
</div>
</div>
</div>
</div>
<input type="button" class="button_ok" name="button_ok" value="OK" (click)="onClickOK($event)" [disabled]="formCheck.invalid">
</form>
ポイントは input
タグの部分。
ここで appNetworkAddressValidator
とディレクティブを指定することで、input フォーム
の内容が NetworkAddressValidatorDirective
に送られる。
input
タグの各属性の説明については関連記事にあげた [Angular] 標準機能を利用して validation を実現する#valildation のための属性を設定 を参照いただきたい。
なお上記参考記事にある「minlength」「maxlength」「pattern」については、今回の記事ではディレクティブ側で実装しているので input
タグの属性から取り除いている。
クラス
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-use-directive',
templateUrl: './use-directive.component.html',
styleUrls: ['./use-directive.component.css']
})
export class UseDirectiveComponent implements OnInit {
/**
* 入力されたアドレス
*
* @type {string}
* @memberof UseDirectiveComponent
*/
public inputAddress: string = '';
/**
* コンストラクタ ( 本コンポーネントではなにもしない )
*
* @memberof UseDirectiveComponent
*/
constructor() { }
/**
* 初期処理 ( 本コンポーネントではなにもしない )
*
* @memberof UseDirectiveComponent
*/
ngOnInit() { }
/**
* OKボタンがクリックされた時のイベントハンドラ
* ここでは単純にアラートを出すだけ
*
* @param {any} $event イベント情報
* @memberof UseDirectiveComponent
*/
public onClickOK($event) {
alert('OK button had clicked.');
}
}
ここではテンプレートの [(ngModel)]="inputAddress"
と紐付けるためのプロパティ inputAddress
を定義していることと、validation による検証エラーが無い場合に OKボタン
をクリックした時の動作を実装しているだけで、ディレクティブに関することで特筆することはなし。
検証結果による描画( エラー表示 )
関連記事であげた [Angular] 標準機能を利用して validation を実現する#エラー表示 と同じ内容なので説明は割愛する。
実行結果
起動直後
入力文字数が短い
入力文字数が長い
入力内容がパターンに一致しない
未入力
正常
ソースコード
今回の記事で作成したコードは こちら にアップしてあるのでご参考まで。