Фильтры на ng-модели во входном
У меня есть текстовый ввод, и я не хочу разрешать пользователям использовать пробелы, и все введенное будет преобразовано в нижний регистр.
Я знаю, что мне не разрешено использовать фильтры на ng-модели, например.
ng-model='tags | lowercase | no_spaces'
Я посмотрел на создание моей собственной директивы, но добавление функций в $parsers
и $formatters
не обновляло входные данные, а только другие элементы, на которых ng-model
.
Как я могу изменить ввод того, что я сейчас набираю?
Я по существу пытаюсь создать функцию тегов, которая работает так же, как и здесь, в StackOverflow.
Ответы
Ответ 1
Я бы предложил посмотреть стоимость модели и обновить ее на chage: http://plnkr.co/edit/Mb0uRyIIv1eK8nTg3Qng?p=preview
Единственная интересная проблема связана с пробелами: в AngularJS 1.0.3 ng-model на входе автоматически обрезается строка, поэтому она не обнаруживает, что модель была изменена, если вы добавляете пробелы в конце или в начале (так что пробелы не автоматически удаленный моим кодом). Но в 1.1.1 существует директива "ng-trim", которая позволяет отключить эту функциональность (commit). Поэтому я решил использовать 1.1.1 для достижения точной функциональности, описанной в вашем вопросе.
Ответ 2
Я считаю, что намерение входа 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 раза:
- Недопустимое значение не распространяется на модель
- Используя директиву, легко добавить эту выборочную проверку на любой вход, не повторяя наблюдателей снова и снова.
Ответ 3
Решением этой проблемы может быть применение фильтров со стороны контроллера:
$scope.tags = $filter('lowercase')($scope.tags);
Не забудьте объявить $filter
как зависимость.
Ответ 4
У меня была аналогичная проблема и я использовал
ng-change="handler(objectInScope)"
в моем обработчике Я вызываю метод objectInScope для правильного изменения (грубый ввод). В контроллере я начал где-то, что
$scope.objectInScope = myObject;
Я знаю, что это не использует причудливые фильтры или наблюдатели... но это просто и отлично работает. Единственным недостатком этого является то, что objectInScope отправляется в вызове обработчику...
Ответ 5
Используйте директиву, которая добавляет в коллекции $formatters и $parsers, чтобы гарантировать, что преобразование выполняется в обоих направлениях.
Подробнее см. этот другой ответ, в том числе ссылку на jsfiddle.
Ответ 6
Вы можете попробовать это
$scope.$watch('tags ',function(){
$scope.tags = $filter('lowercase')($scope.tags);
});
Ответ 7
Если вы выполняете сложную асинхронную проверку ввода, возможно, стоит отбросить ng-model
на один уровень как часть пользовательского класса со своими собственными методами проверки.
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) })
};
})();
Ответ 8
Если вы используете только поле ввода только для чтения, вы можете использовать ng-значение с фильтром.
например:
ng-value="price | number:8"