web-dev-qa-db-ja.com

入力のng-modelにフィルタをかける

私はテキストを入力していますが、ユーザーにスペースの使用を許可したくないので、入力したものはすべて小文字になります。

私は、例えばng-modelでフィルタを使うことが許可されていないことを知っています。

ng-model='tags | lowercase | no_spaces'

私は自分自身のディレクティブを作成することを検討しましたが、$parsers$formattersに関数を追加しても入力は更新されず、ng-modelを持つ他の要素のみが更新されました。

現在入力中の入力を変更するにはどうすればよいですか。

私は本質的にはStackOverflowでここにあるもののように機能する 'タグ'機能を作成しようとしています。

122
Andrew WC Brown

私はモデルの値を監視し、変更時にそれを更新することをお勧めします: http://plnkr.co/edit/ Mb0uRyIIv1eK8nTg3Qng?p =プレビュー

唯一の興味深い問題はスペースに関するものです。AngularJS 1.0.3では、入力時のng-modelは文字列を自動的にトリミングするので、最後または最初にスペースを追加してもモデルが変更されたことは検出されません。コード)。しかし1.1.1にはこの機能を無効にすることができる 'ng-trim'ディレクティブがあります( commit )。だから私はあなたがあなたの質問で述べた正確な機能性を達成するために1.1.1を使うことにしました。

28

AngularJS入力とngModelディレクティブの意図は、無効な入力がモデルに含まれないようにすることです。モデルは常に有効であるべきです。モデルが無効であることの問題は、無効なモデルに基づいて(不適切な)アクションを起動して実行するウォッチャーがいる可能性があることです。

ご覧のとおり、ここでの正しい解決策は$parsersパイプラインにプラグインし、無効な入力がモデルに入らないようにすることです。 $parsersを使ってどのようにして物事にアプローチしようとしたのか、あるいはまったくうまくいかなかったのかわかりませんが、ここであなたの問題を解決する簡単な指示文(または少なくとも私の問題理解)があります。

app.directive('customValidation', function(){
   return {
     require: 'ngModel',
     link: function(scope, element, attrs, modelCtrl) {

       modelCtrl.$parsers.Push(function (inputValue) {

         var transformedInput = inputValue.toLowerCase().replace(/ /g, ''); 

         if (transformedInput!=inputValue) {
           modelCtrl.$setViewValue(transformedInput);
           modelCtrl.$render();
         }         

         return transformedInput;         
       });
     }
   };
});

上記の指令が宣言されるとすぐに、次のように使用できます。

<input ng-model="sth" ng-trim="false" custom-validation>

@Valentyn Shybanovによって提案された解決策のように、入力の最初/最後にスペースを入れたくない場合はng-trimディレクティブを使う必要があります。

このアプローチの利点は2つあります。

  • 無効な値はモデルに伝達されません
  • ディレクティブを使用すると、ウォッチャーを何度も複製することなく、このカスタム検証を任意の入力に簡単に追加できます。
203

この問題を解決するには、コントローラ側にフィルタを適用します。

$scope.tags = $filter('lowercase')($scope.tags);

$filterを依存関係として宣言することを忘れないでください。

読み取り専用の入力フィールドを使用している場合は、ng-valueをfilterと共に使用できます。

例えば:

ng-value="price | number:8"
5

変換が両方向に確実に実行されるように、$ formattersコレクションと$ parsersコレクションの両方に追加するディレクティブを使用してください。

Jsfiddleへのリンクを含む詳細については、 この他の答え を参照してください。

4
Scott Munro

私は同様の問題を抱えて使用しました

ng-change="handler(objectInScope)" 

私のハンドラーでは、objectInScopeのメソッドを呼び出して自分自身を正しく変更します(粗入力)。コントローラで私はどこかでそれを始めました

$scope.objectInScope = myObject; 

私はこれがどんな派手なフィルタもウォッチャーも使用しないのを知っています...しかしそれは簡単で素晴らしい仕事をします。唯一の欠点は、objectInScopeがハンドラへの呼び出しで送信されることです。

3
wojjas

複雑で非同期の入力検証をしている場合は、独自の検証メソッドを使用してカスタムクラスの一部としてng-modelを1つ上のレベルに抽象化することをお勧めします。

https://plnkr.co/edit/gUnUjs0qHQwkq2vPZlpO?p=preview

html

<div>

  <label for="a">input a</label>
  <input 
    ng-class="{'is-valid': vm.store.a.isValid == true, 'is-invalid': vm.store.a.isValid == false}"
    ng-keyup="vm.store.a.validate(['isEmpty'])"
    ng-model="vm.store.a.model"
    placeholder="{{vm.store.a.isValid === false ? vm.store.a.warning : ''}}"
    id="a" />

  <label for="b">input b</label>
  <input 
    ng-class="{'is-valid': vm.store.b.isValid == true, 'is-invalid': vm.store.b.isValid == false}"
    ng-keyup="vm.store.b.validate(['isEmpty'])"
    ng-model="vm.store.b.model"
    placeholder="{{vm.store.b.isValid === false ? vm.store.b.warning : ''}}"
    id="b" />

</div>

コード

(function() {

  const _ = window._;

  angular
    .module('app', [])
    .directive('componentLayout', layout)
    .controller('Layout', ['Validator', Layout])
    .factory('Validator', function() { return Validator; });

  /** Layout controller */

  function Layout(Validator) {
    this.store = {
      a: new Validator({title: 'input a'}),
      b: new Validator({title: 'input b'})
    };
  }

  /** layout directive */

  function layout() {
    return {
      restrict: 'EA',
      templateUrl: 'layout.html',
      controller: 'Layout',
      controllerAs: 'vm',
      bindToController: true
    };
  }

  /** Validator factory */  

  function Validator(config) {
    this.model = null;
    this.isValid = null;
    this.title = config.title;
  }

  Validator.prototype.isEmpty = function(checkName) {
    return new Promise((resolve, reject) => {
      if (/^\s+$/.test(this.model) || this.model.length === 0) {
        this.isValid = false;
        this.warning = `${this.title} cannot be empty`;
        reject(_.merge(this, {test: checkName}));
      }
      else {
        this.isValid = true;
        resolve(_.merge(this, {test: checkName}));
      }
    });
  };

  /**
   * @memberof Validator
   * @param {array} checks - array of strings, must match defined Validator class methods
   */

  Validator.prototype.validate = function(checks) {
    Promise
      .all(checks.map(check => this[check](check)))
      .then(res => { console.log('pass', res)  })
      .catch(e => { console.log('fail', e) })
  };

})();
1
Daniel Lizik

あなたはこれを試すことができます

$scope.$watch('tags ',function(){

    $scope.tags = $filter('lowercase')($scope.tags);

});
0
Nikhil Mahirrao