AngularJSで半角数値しか入力出来ないディレクティブを作ってみた
WEBアプリのバリデーション処理はめんどくさい
とある業務系のアプリを作成していて、 数値を入力してもらう箇所に普通の文字列を入れられ挙動がおかしくなることがあった
それぐらいバリデーション入れろよ・・・とツッコミが聞こえそうだけど、仕様が変わりすぎて追いつけなかったんや・・・
バリデーションのパターンを作るのも面倒なので、「そもそも数値以外入力できない」ようにしたい
なので数値しか入力できないディレクティブを作ってみた
ちょっとこだわった点
- そもそも半角数値以外はインプットさせない
- 全角の数値を自動で変換する
- メッセージ等の見た目を良くしたいので、AngularStrapを利用
できたもの 実はこれ作ったの多分1年前
↓HTML
<div class="container" ng-app="App"> <form> <p ng-init="hoge=234"> <label for="age">強制半角フォーム : </label> <input type="text" id="age" name="age" ng-model="hoge" ng-number-only/> </p> <p> model:{{hoge}} </p> </form> </div>
↓Javascript(jQuery、AngularJS、AngularStrap)
(function(window) { 'use strict'; window.isNullOrEmpty = function(target) { if (typeof target === 'undefined' || target === null || target === '') { return true; } else { return false; } }; })(window); (function(window, angular) { 'use strict'; var module = angular.module('App', ['mgcrea.ngStrap']); module.directive('ngNumberOnly', [ '$tooltip', function($tooltip) { return { 'restrict': 'A', 'require': '?ngModel', 'link': function(scope, el, atts, ctrl) { if (el[0].localName.toLowerCase() !== 'input' || atts.type !== 'text') { throw new Error('Support Only "input[type=text]"'); } var title = '数字を入力してください'; if (!window.isNullOrEmpty(atts.title)) { title = atts.title; } var tooltipParams = { 'placement': 'right', 'trigger': 'manual', 'title': title }; var myTooltip = $tooltip(el, tooltipParams); el.on('input propertychange', function(e) { var caretPosition = this.selectionStart; //半角0-9以外は if (/[^0-9]+/g.test(this.value)) { // 全角0-9は半角0-9へ this.value = this.value.replace(/[0-9]/g, function(s) { myTooltip.hide(); caretPosition = caretPosition + 1; return String.fromCharCode(s.charCodeAt(0) - 0xFEE0); }); // 0-9以外はアラートのツールチップを出す if (/[^0-9]+/g.test(this.value)) { myTooltip.show(); } // 半角英字記号半角スペースはキャレット(I)が右に動いてしまうので一つ左へ移動 if(/[A-Za-z!"#$%&'()\*\+\-\.,\/:;<=>?@\[\\\]^_`{|}~\s]/.test(this.value)){ caretPosition = caretPosition-1; } //0-9以外を全て空("")に置き換え this.value = this.value.replace(/[^0-9]+/g, ''); //キャレットを元の位置に戻す this.selectionStart = caretPosition; this.selectionEnd = caretPosition; } else { myTooltip.hide(); } if (ctrl != null) { // jQueryやイベントハンドラ(AngularJSの管轄外)から要素のvalueを変更すると、 // modelに値が反映されない。htmlのvalueをmodelに反映するため、$setViewValueを呼び出す。 // (コメントアウトすると分かりやすい) ctrl.$setViewValue(this.value); } }); el.on('blur', function(e) { myTooltip.hide(); }); } }; } ]); })(window, window.angular);
↓CSS
.container{ margin:50px; width:300px; height:300px; border:1px solid #ddd; padding:30px; }
↓全角を半角にするのに参考にしたサイト
動作確認
作っていてハマった点
- キャレットの位置が変わる
- Angularのモデルの値が変わらない
Angularのモデルの値が変わらないについて
AngularJSの管理外から値の変更があると、AngularJSが検知できない
scopeで検知できない感じ
AngularJSの管理外から値の変更した場合は、「$setViewValue」というメソッドを呼び出す必要がある。
↓参考サイト
ngModel.NgModelController | AngularJS 1.2 日本語リファレンス | js STUDIO
わかったことまとめ
- AngularJSの管理外(scope外)から値の変更した場合は、「$setViewValue」を使用する
- 全角数値を半角数値に変換する方法
Angular2がリリースされているけど、使ったことは無い
Angular2にこういうハマりそうなポイントがなければいいなー