Angular.js: ngModelController $ parsers関数が一貫して呼び出されない

作成日 2015年08月10日  ·  3コメント  ·  ソース: angular/angular.js

ngModelController $ parser関数を使用して入力値を特定のルールに準拠するように強制する場合、$ parser関数は、繰り返される「無効な」キーストロークで呼び出されないため、無効な文字が追加される可能性があります。

以下の例では、ディレクティブは数字以外のすべての文字を削除することを目的としています。 ほとんどの場合は機能しますが、同じ数字以外の文字を2回続けて入力すると(たとえば、「xx」)、単一の「x」が入力に表示されます。 「x」を3回入力すると、「x」が削除されます。

失敗したテストケースも含めました。

すべてのコードはhttp://codepen.io/visnup/pen/YXgLVq?editors=101で実行されてい

ディレクティブの使用法

<div ng-app="digits">
  <input ng-model="number" placeholder="digits only" digits-only />
</div>

指令

angular
  .module('digits', [])
  .directive('digitsOnly', function() {
    return {
      require: 'ngModel',
      link: function link(scope, element, attrs, ngModel) {
        ngModel.$parsers.push(function(value) {
          var numbers = value.replace(/\D/g, '');
          element.val(numbers);
          return numbers;
        });
      }
    };
  });

失敗したテストケース

describe('digits', function() {
  beforeEach(angular.mock.module('digits'));

  let scope, input;
  beforeEach(inject(function($compile, $rootScope) {
    scope = $rootScope;
    input = $compile('<input ng-model="number" digits-only />')(scope);
  }));

  // works
  it('should block non-digits', function() {
    input.val('111x');
    input.triggerHandler('input');
    expect(input.val()).to.equal('111');
  });

  // doesn't work
  it('should not flash incorrect input', function() {
    input.val('111x');
    input.triggerHandler('input');
    input.val('111x');
    input.triggerHandler('input');
    expect(input.val()).to.equal('111');
  });
});

これは以前#10700で取り上げられました。

最も参考になるコメント

$parsersは、 $modelValueとして「保存」する前に$viewValueを変換(のコピー)するためのものです(これがあなたの例です)。 実際には$viewValue変更していません(ただし、要素の値は更新されます)。これにより、予期しない動作が発生します。

これは、例でよりよく説明されています。

  1. ユーザーが「1」を入力します。

    • _Before_解析:



      • $modelValue :未定義


      • $viewValue :1


      • element.val() :1



    • _After_解析:



      • $modelValue :1


      • $viewValue :1


      • element.val() :1



  2. ユーザーは「x」を入力します。

    • _Before_解析:



      • $modelValue :1


      • $viewValue :1x


      • element.val() :1x



    • _After_解析:



      • $modelValue :1


      • $viewValue :1x(パーサーfnから更新されることはありません)


      • element.val() :1(パーサーfnから更新)



  3. ユーザーは「x」を入力します。

    • _Before_解析:



      • $modelValue :1


      • $viewValue :1x(以前の$viewValue1xだったため、変更は検出されません


      • element.val() :1x



    • _いいえ_解析( $viewValue変更が検出されなかったため)!!!



      • $modelValue :1


      • $viewValue :1x


      • element.val() :1x



  4. ユーザーは「x」を入力します。

    • _Before_解析:



      • $modelValue :1


      • $viewValue :1xx


      • element.val() :1xx



    • _After_解析:



      • $modelValue :1


      • $viewValue :1xx


      • element.val() :1



  5. _(...ステップ(2)に戻る...)_

これを適切に処理するには、いくつかの「角度のある」方法があります。例:

ngModel.$parsers.push(function (value) {
  var numbers = value.replace(/\D/g, '');
  if (numbers !== value) {
    ngModel.$setViewValue(numbers);   // Update the `$viewValue`
    ngModel.$render();                // Update the element's displayed value
  }
  return numbers;
});

更新されたペン


すべてが期待どおりに機能しているように見えるので、これを閉じます。
@visnup 、不明な点がある場合は、

全てのコメント3件

$parsersは、 $modelValueとして「保存」する前に$viewValueを変換(のコピー)するためのものです(これがあなたの例です)。 実際には$viewValue変更していません(ただし、要素の値は更新されます)。これにより、予期しない動作が発生します。

これは、例でよりよく説明されています。

  1. ユーザーが「1」を入力します。

    • _Before_解析:



      • $modelValue :未定義


      • $viewValue :1


      • element.val() :1



    • _After_解析:



      • $modelValue :1


      • $viewValue :1


      • element.val() :1



  2. ユーザーは「x」を入力します。

    • _Before_解析:



      • $modelValue :1


      • $viewValue :1x


      • element.val() :1x



    • _After_解析:



      • $modelValue :1


      • $viewValue :1x(パーサーfnから更新されることはありません)


      • element.val() :1(パーサーfnから更新)



  3. ユーザーは「x」を入力します。

    • _Before_解析:



      • $modelValue :1


      • $viewValue :1x(以前の$viewValue1xだったため、変更は検出されません


      • element.val() :1x



    • _いいえ_解析( $viewValue変更が検出されなかったため)!!!



      • $modelValue :1


      • $viewValue :1x


      • element.val() :1x



  4. ユーザーは「x」を入力します。

    • _Before_解析:



      • $modelValue :1


      • $viewValue :1xx


      • element.val() :1xx



    • _After_解析:



      • $modelValue :1


      • $viewValue :1xx


      • element.val() :1



  5. _(...ステップ(2)に戻る...)_

これを適切に処理するには、いくつかの「角度のある」方法があります。例:

ngModel.$parsers.push(function (value) {
  var numbers = value.replace(/\D/g, '');
  if (numbers !== value) {
    ngModel.$setViewValue(numbers);   // Update the `$viewValue`
    ngModel.$render();                // Update the element's displayed value
  }
  return numbers;
});

更新されたペン


すべてが期待どおりに機能しているように見えるので、これを閉じます。
@visnup 、不明な点がある場合は、

この問題は、新しい$ viewValueがコミットされていないときにキーストロークを繰り返すと発生します。 $ parsersコレクションでの$ setViewValueの推奨される使用は、一部のフォーラムで$ viewValueを直接設定することを優先して回避される場合があります。

以下も機能します。

ngModel.$viewValue = numbers;   // Update the '$viewValue'
ngModel.$commitViewValue(); //update $$lastCommittedViewValue
ngModel.$render();                // Update the element's displayed value

再帰の落とし穴をまったく回避したい場合は、カプセル化に違反してもかまわないのであれば、$$ lastCommittedViewValueを直接更新できます。 残念ながら、これらの関数で無限再帰が発生すると、最大発生後もコンソールに報告されない場合があります。 テスト中にブレークポイントを設定し、再帰を確認するのに数分かかる価値があります。

@gkalpak@snaptechの説明を組み合わせると、黄金の解決策になります:):+ 1:

このページは役に立ちましたか?
0 / 5 - 0 評価