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.
$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:
$modelValue
: tidak ditentukan$viewValue
: 1element.val()
: 1$modelValue
: 1$viewValue
: 1element.val()
: 1$modelValue
: 1$viewValue
: 1xelement.val()
: 1x$modelValue
: 1$viewValue
: 1x (tidak pernah diperbarui dari parser fn)element.val()
: 1 (diperbarui dari parser fn)$modelValue
: 1$viewValue
: 1x (Tidak ada perubahan yang terdeteksi, karena sebelumnya $viewValue
juga 1x
)element.val()
: 1x$viewValue
) !!!$modelValue
: 1$viewValue
: 1xelement.val()
: 1x$modelValue
: 1$viewValue
: 1xxelement.val()
: 1xx$modelValue
: 1$viewValue
: 1xxelement.val()
: 1Ada 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;
});
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:
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:
$modelValue
: tidak ditentukan$viewValue
: 1element.val()
: 1$modelValue
: 1$viewValue
: 1element.val()
: 1$modelValue
: 1$viewValue
: 1xelement.val()
: 1x$modelValue
: 1$viewValue
: 1x (tidak pernah diperbarui dari parser fn)element.val()
: 1 (diperbarui dari parser fn)$modelValue
: 1$viewValue
: 1x (Tidak ada perubahan yang terdeteksi, karena sebelumnya$viewValue
juga1x
)element.val()
: 1x$viewValue
) !!!$modelValue
: 1$viewValue
: 1xelement.val()
: 1x$modelValue
: 1$viewValue
: 1xxelement.val()
: 1xx$modelValue
: 1$viewValue
: 1xxelement.val()
: 1Ada beberapa cara "Sudut" untuk menangani ini dengan benar, misalnya:
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.