入力のngモデルのフィルター


124

テキスト入力があり、ユーザーがスペースを使用できるようにしたくないので、入力したすべての文字が小文字に変換されます。

ng-modelなどでフィルターを使用することは許可されていません。

ng-model='tags | lowercase | no_spaces'

私は自分のディレクティブを作成するだけに機能を追加することを見て$parsers$formatters入力し、持っていた唯一の他の要素は更新されませんでしたng-modelそれをし。

現在入力している入力を変更するにはどうすればよいですか?

私は基本的に、ここでStackOverflowの機能と同じように機能する「タグ」機能を作成しようとしています。


:助け-変更をngの$タイムアウトを使用している場合を参照してください(...、0) stackoverflow.com/questions/12176925/...
マーク・Rajcok

回答:


28

私はモデル値を監視し、変更時に更新することをお勧めします:http ://plnkr.co/edit/Mb0uRyIIv1eK8nTg3Qng?p=preview

唯一の興味深い問題はスペースに関するものです。AngularJS1.0.3では、入力のng-modelが文字列を自動的にトリミングするため、末尾または先頭にスペースを追加した場合にモデルが変更されたことを検出しません(したがって、スペースは自動的に削除されません)コード)。しかし、1.1.1には、この機能を無効にできる「ng-trim」ディレクティブがあります(commit)。だから私はあなたがあなたの質問で説明した正確な機能を実現するために1.1.1を使うことにしました。


これはまさに私が探していたものでした。私はすでにangularjs 1.1.1を使用していることがわかりました
Andrew WC Brown

@Valentyn、あなたの解決策は上記のコメントで参照したSOの質問に適用されました。ありがとう。 stackoverflow.com/questions/12176925/...
マーク・Rajcok

このソリューションは悪い副作用をもたらす可能性があります。以下の他の回答を参照してください。これにはディレクティブを使用する必要があります
pilavdzice

内部からスコープ変数を再割り当て$watchすると、リスナーが強制的に再度呼び出されます。単純なケース(フィルターがべき等)の場合、変更ごとにフィルターが2回実行されることになります。
転生

204

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つあります。

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

1
私はトリッキーな部分はしていたことを確認していますmodelCtrl.$setViewValue(transformedInput); modelCtrl.$render();ドキュメントに有用であろうリンク:docs.angularjs.org/api/ng.directive:ngModel.NgModelController 私solitionは、そのスコープのプロパティは変更されている場合もある「守る」の1つの単語だけでなく、ビューからと私の方法はこれをカバーします。スコープをどのように変更できるかは、実際の状況次第だと思います。
Valentyn Shybanov 2013年

2
あなたの例で「modelCtrl」は何を指しますか?
GSto 2013年

4
inputValueはどこから取得しますか?
Dofs 2013年

2
@GSto modelCtrlは、ディレクティブで必要なコントローラーです。(require 'ngModel'
Nate-Wilkins、2014

7
無効な文字を入力するたびにカーソルがテキストフィールドの最後にジャンプし、「world」と書き込んで「HeLLo world」に変更してみてください。
Hafez Divandari 2015年

23

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

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

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


4
しかし、適切に更新するには、$ watchが必要です。
氏ミッケル

これは一度だけ実行されます。また、ウォッチに追加することは適切な解決策ではありません。これは、最初であっても、モデルが無効になる可能性があるためです。正しい解決策は、モデルの$ parsersに追加することです。
icfantv 2016

4
あなたは私の答えを好きにする必要はありませんが、それはそれが間違っているという意味ではありません。反対票を投じる前に自分のエゴをチェックしてください。
icfantv 2016


4

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

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


3

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

ng-change="handler(objectInScope)" 

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

$scope.objectInScope = myObject; 

派手なフィルターやウォッチャーを使用していないことはわかっていますが、シンプルで優れています。これの唯一の欠点は、objectInScopeがハンドラーの呼び出しで送信されることです...


1

複雑な非同期入力検証を行う場合は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) })
  };

})();

0

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

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

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

});
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.