Сохранение положения курсора с помощью угловых
Следующий фрагмент делает то, что я хочу для input
, т.е. удаляет все небуквенные символы, преобразует их в верхний регистр и сохраняет позицию курсора.
element = $(element);
element.keyup(function() {
var x = element.val();
var y = x && x.toUpperCase().replace(/[^A-Z\d]/g, '');
if (x===y) return;
var start = this.selectionStart;
var end = this.selectionEnd + y.length - x.length;
element.val(y);
this.setSelectionRange(start, end);
});
Я разместил этот фрагмент в link
директивы, и он работает... в основном.
Проблема заключается в том, что модель angular
видит значение до того, как будет применено изменение. Я попытался использовать Google для использования $apply
или $digest
или что-то здесь, но ничего не получилось.
(На самом деле, я каким-то образом справился с этим, но потом содержимое было повторно отображено, и я потерял позицию. Я не могу воспроизвести его, но это было недостаточно.)
Ответы
Ответ 1
Способ сделать это, когда
- Ввод очищается только один раз
-
ngChange
на входе затем запускается только один раз
должен использовать массив $parsers
, который предоставляет ngModelController. Он предназначен как место для воздействия на значение модели (через его возвращаемое значение), но также может использоваться как слушатель для ввода событий.
app.directive('cleanInput', function() {
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModelController) {
var el = element[0];
function clean(x) {
return x && x.toUpperCase().replace(/[^A-Z\d]/g, '');
}
ngModelController.$parsers.push(function(val) {
var cleaned = clean(val);
// Avoid infinite loop of $setViewValue <-> $parsers
if (cleaned === val) return val;
var start = el.selectionStart;
var end = el.selectionEnd + cleaned.length - val.length;
// element.val(cleaned) does not behave with
// repeated invalid elements
ngModelController.$setViewValue(cleaned);
ngModelController.$render();
el.setSelectionRange(start, end);
return cleaned;
});
}
}
});
Однако я не уверен, что это использование $parsers
- это немного взлома. Директива может использоваться как:
<input type="text" clean-input ng-model="name">
или если вы хотите использовать функцию ngChange
:
<input type="text" clean-input ng-model="name" ng-change="onChange()">
Это можно увидеть в действии в http://plnkr.co/edit/dAJ46XmmC49wqTgdp2qz?p=preview
Ответ 2
Основные вещи, которые необходимы:
-
Требовать ngModelController
, чтобы иметь возможность вызывать его методы и получать/устанавливать его значения. В частности...
-
Замените вызов element.val(y)
на
ngModelController.$setViewValue(y);
ngModelController.$render();
Я думаю, что я должен признать, что я не совсем уверен в внутренней работе ngModelController
, чтобы понять, почему это необходимо.
-
Необязательно, но получение существующего значения в представлении element.val()
можно сделать следующим образом:
ngModelController.$viewValue;
который по меньшей мере более согласуется с способом установки значения представления.
-
Опять необязательно, но прослушивание события input
делает интерфейс немного приятнее, поскольку он, похоже, немного срабатывает перед событием keyup
, поэтому вы не получаете вспышку необработанного ввода.
-
Добавление в массив $parsers
для обработки ввода, похоже, останавливает любые обратные вызовы ngChange
, которые запускаются для не обработанной версии ввода.
ngModelController.$parsers.push(function(val) {
// Return the processed value
})
Объединяя все это как пользовательскую директиву:
app.directive('cleanInput', function() {
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModelController) {
function clean(x) {
return x && x.toUpperCase().replace(/[^A-Z\d]/g, '');
}
ngModelController.$parsers.push(function(val) {
return clean(val);
})
element.on('input', function() {
var x = ngModelController.$viewValue;
var y = clean(x);
var start = this.selectionStart;
var end = this.selectionEnd + y.length - x.length;
ngModelController.$setViewValue(y);
ngModelController.$render();
this.setSelectionRange(start, end);
});
}
}
});
который можно использовать как:
<input type="text" clean-input ng-model="name">
или если вы хотите использовать функцию ngChange
:
<input type="text" clean-input ng-model="name ng-change="onChange()">
и рассматривается в действии на http://plnkr.co/edit/FymZ8QEKwj2xXTmaExrH?p=preview
Изменить: добавьте часть в массив $parsers
. Должен признаться, это был ответ @Engineer, который заставлял меня думать об этом.