Angular.js: ngModelController $parser fungsi tidak dipanggil secara konsisten

Dibuat pada 10 Agu 2015  ·  3Komentar  ·  Sumber: angular/angular.js

Saat menerapkan nilai input agar sesuai dengan aturan tertentu menggunakan fungsi ngModelController $parser, fungsi $parser tidak dipanggil pada penekanan tombol "tidak valid" berulang, yang memungkinkan karakter yang tidak valid ditambahkan.

Dalam contoh di bawah, direktif dimaksudkan untuk menghapus semua karakter non-digit. sebagian besar berfungsi, tetapi jika Anda mengetik karakter non-digit yang sama dua kali berturut-turut (mis. 'xx'), satu 'x' akan muncul di input. jika Anda mengetik 'x' untuk ketiga kalinya, itu akan menghapus 'x'.

Saya juga menyertakan kasus uji yang gagal.

semua kode berjalan di http://codepen.io/visnup/pen/YXgLVq?editors=101

penggunaan direktif

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

pengarahan

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

kasus uji gagal

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

Ini telah diangkat sebelumnya di #10700.

Komentar yang paling membantu

$parsers adalah untuk mengubah (salinan) $viewValue sebelum "menyimpan" sebagai $modelValue (dan itulah yang dilakukan oleh contoh Anda). Itu sebenarnya tidak mengubah $viewValue (meskipun itu memperbarui nilai elemen), yang mengarah ke perilaku yang tidak terduga.

Ini lebih baik diilustrasikan dengan sebuah contoh:

  1. Pengguna memasukkan '1'.

    • _Sebelum_ menguraikan:



      • $modelValue : tidak ditentukan


      • $viewValue : 1


      • element.val() : 1



    • _Setelah_ penguraian:



      • $modelValue : 1


      • $viewValue : 1


      • element.val() : 1



  2. Pengguna memasukkan 'x'.

    • _Sebelum_ menguraikan:



      • $modelValue : 1


      • $viewValue : 1x


      • element.val() : 1x



    • _Setelah_ penguraian:



      • $modelValue : 1


      • $viewValue : 1x (tidak pernah diperbarui dari parser fn)


      • element.val() : 1 (diperbarui dari parser fn)



  3. Pengguna memasukkan 'x'.

    • _Sebelum_ menguraikan:



      • $modelValue : 1


      • $viewValue : 1x (Tidak ada perubahan yang terdeteksi, karena sebelumnya $viewValue juga 1x )


      • element.val() : 1x



    • _No_ parsing (karena tidak ada perubahan yang terdeteksi di $viewValue ) !!!



      • $modelValue : 1


      • $viewValue : 1x


      • element.val() : 1x



  4. Pengguna memasukkan 'x'.

    • _Sebelum_ menguraikan:



      • $modelValue : 1


      • $viewValue : 1xx


      • element.val() : 1xx



    • _Setelah_ penguraian:



      • $modelValue : 1


      • $viewValue : 1xx


      • element.val() : 1



  5. _(...kembali ke langkah (2)...)_

Ada beberapa cara "Sudut" untuk menangani ini dengan benar, misalnya:

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

Pena yang diperbarui


Saya menutup ini, karena semuanya tampak berfungsi seperti yang diharapkan.
@visnup , jangan ragu untuk menindaklanjuti dengan pertanyaan jika ada yang tidak jelas.

Semua 3 komentar

$parsers adalah untuk mengubah (salinan) $viewValue sebelum "menyimpan" sebagai $modelValue (dan itulah yang dilakukan oleh contoh Anda). Itu sebenarnya tidak mengubah $viewValue (meskipun itu memperbarui nilai elemen), yang mengarah ke perilaku yang tidak terduga.

Ini lebih baik diilustrasikan dengan sebuah contoh:

  1. Pengguna memasukkan '1'.

    • _Sebelum_ menguraikan:



      • $modelValue : tidak ditentukan


      • $viewValue : 1


      • element.val() : 1



    • _Setelah_ penguraian:



      • $modelValue : 1


      • $viewValue : 1


      • element.val() : 1



  2. Pengguna memasukkan 'x'.

    • _Sebelum_ menguraikan:



      • $modelValue : 1


      • $viewValue : 1x


      • element.val() : 1x



    • _Setelah_ penguraian:



      • $modelValue : 1


      • $viewValue : 1x (tidak pernah diperbarui dari parser fn)


      • element.val() : 1 (diperbarui dari parser fn)



  3. Pengguna memasukkan 'x'.

    • _Sebelum_ menguraikan:



      • $modelValue : 1


      • $viewValue : 1x (Tidak ada perubahan yang terdeteksi, karena sebelumnya $viewValue juga 1x )


      • element.val() : 1x



    • _No_ parsing (karena tidak ada perubahan yang terdeteksi di $viewValue ) !!!



      • $modelValue : 1


      • $viewValue : 1x


      • element.val() : 1x



  4. Pengguna memasukkan 'x'.

    • _Sebelum_ menguraikan:



      • $modelValue : 1


      • $viewValue : 1xx


      • element.val() : 1xx



    • _Setelah_ penguraian:



      • $modelValue : 1


      • $viewValue : 1xx


      • element.val() : 1



  5. _(...kembali ke langkah (2)...)_

Ada beberapa cara "Sudut" untuk menangani ini dengan benar, misalnya:

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

Pena yang diperbarui


Saya menutup ini, karena semuanya tampak berfungsi seperti yang diharapkan.
@visnup , jangan ragu untuk menindaklanjuti dengan pertanyaan jika ada yang tidak jelas.

Masalah ini terjadi dengan penekanan tombol berulang setiap kali $viewValue baru tidak dikomit. Penggunaan $setViewValue yang disarankan dalam koleksi $parsers terkadang dihindari dengan preferensi untuk langsung menyetel $viewValue di beberapa forum.

Berikut ini juga akan berfungsi:

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

Jika Anda ingin menghindari perangkap rekursi sama sekali maka $$lastCommittedViewValue dapat diperbarui secara langsung jika Anda tidak keberatan melanggar enkapsulasi. Sayangnya ketika rekursi tak terbatas memang terjadi dalam fungsi-fungsi ini, saya telah melihat kasus di mana itu tidak dilaporkan ke konsol bahkan setelah kejadian maksimal. Layak mengambil beberapa menit untuk menempatkan break point selama pengujian dan memverifikasi setiap rekursi.

Penjelasan @gkalpak dan @snaptech digabungkan bersama menjadi solusi emas :) :+1:

Apakah halaman ini membantu?
0 / 5 - 0 peringkat