フォームの金額入力とかで、入力時は1000000
でフォーカスが外れたら1,000,000
といったようにすることで大きい値でも判断しやすくなり入力ミスを減らしたい…..
今回は上記のような要望がちょろっと上がったので実際に試してみました
フォーカスが外れたら3桁区切りでカンマが表示されるようにしてみます
動作としてはこんな感じ
方針
focus,blurのイベント発生時にinputのvalueの値を変える感じになりそうですね
普通にcomponentで実装しても良さそうですが勉強がてらdirectiveで実装してみます
ということで、attribute directiveを使ってfocus,blur時に処理を追加してあげるようにしてみます
やることは下記
- pipeの実装
数値フォーマットの変更自体がangularのDecimalPipeの機能で事足りるが、カンマ区切りから数値に戻す方の処理がないためその処理だけ追加したpipeを実装する
- directiveの実装
focus,blurイベントで発火するInput用のdirectiveを実装する
pipeの実装
ng g pipe pipes/number-input
angular-cliのジェネレータで作られたpipeにはtransformメソッドのみなのでblur時に値を戻すメソッドを独自に実装します
- src/app/pipes/number-input.pipe.ts
import { Pipe, PipeTransform } from '@angular/core'; import { DecimalPipe } from '@angular/common'; @Pipe({ name: 'numberInput' }) export class NumberInputPipe implements PipeTransform { transform(value: any, digits?: string): string { return new DecimalPipe('ja').transform(value,digits); } parse(value: string): string { return value.replace(/,/g,''); } }
pipeはテンプレート側からの呼び出しのイメージが強いがts側からでも呼び出すことができる
DecimalPipeをnewする際になぜかlocaleを引数として渡さないといけないようだった
実装の中身まで追ってないけどなんでなんだろう。。。。。
directiveの実装
focusイベントとblurイベントのイベントリスナを設定してそれぞれ対応する処理を記述します
ng g directive directives/number-input
- src/app/directives/number-input.directive.ts
import {OnInit, Directive, ElementRef, HostListener} from '@angular/core'; import {NumberInputPipe} from "../pipes/number-input.pipe"; @Directive({ selector: '[numberInput]' }) export class NumberInputDirective implements OnInit { private element: HTMLInputElement; private digits: string = '1.0-0'; constructor( private elementRef: ElementRef, private numberInputPipe: NumberInputPipe, ) { this.element = this.elementRef.nativeElement; } ngOnInit(){ this.element.value = this.numberInputPipe.transform(this.element.value,this.digits); } @HostListener("focus", ["$event.target.value"]) onFocus(value){ this.element.value = this.numberInputPipe.parse(value); } @HostListener("blur", ["$event.target.value"]) onBlur(value){ this.element.value = this.numberInputPipe.transform(value,this.digits); } }
elementRef.nativeElementで自身を参照できるようにする
this.elementは HTMLInputElementなのでthis.element.value
で入力されたテキストの値を参照できるようになる
あとはイベント発火時に最初に実装したpipe経由でフォーマットの変換を行う処理を実装する
NgModuleへの追加
これはangular-cli経由で追加したらよしなにしてくれるので特に気にすることはないと思っていたが、pipeに関しては自分でprovidersに定義し直す必要があった
- src/app.module.ts
@NgModule({ declarations: [ ..... ..... NumberInputDirective ], ..... ..... providers: [ NumberInputPipe ] })
- 呼び出し側(html)
<input type="text" numberInput value="10000">
これだけで終わり!
簡単ですね
まとめ
サンプルではvalidationしていないので実際に使うにはもう一手間必要ですが簡単に実装することができました
今回のようなpipeとdirectiveの組み合わせで他にも細かな共通処理を切り出すことが可能になりそうですね
感想としては、今回directiveに関して色々調べたことでこのライブラリはこれ使ってるのね!みたなのが結構あって勉強になりました。