もなかアイスの試食品

「とりあえずやってみたい」そんな気持ちが先走りすぎて挫折が多い私のメモ書きみたいなものです.

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;
}

↓全角を半角にするのに参考にしたサイト

www.nishishi.com

動作確認


作っていてハマった点

  • キャレットの位置が変わる
  • Angularのモデルの値が変わらない

Angularのモデルの値が変わらないについて

AngularJSの管理外から値の変更があると、AngularJSが検知できない

scopeで検知できない感じ

AngularJSの管理外から値の変更した場合は、「$setViewValue」というメソッドを呼び出す必要がある。

↓参考サイト

ngModel.NgModelController | AngularJS 1.2 日本語リファレンス | js STUDIO

わかったことまとめ

  • AngularJSの管理外(scope外)から値の変更した場合は、「$setViewValue」を使用する
  • 全角数値を半角数値に変換する方法

Angular2がリリースされているけど、使ったことは無い

Angular2にこういうハマりそうなポイントがなければいいなー