filters on ng-model in an input
Solution 1
I would suggest to watch model value and update it upon chage: http://plnkr.co/edit/Mb0uRyIIv1eK8nTg3Qng?p=preview
The only interesting issue is with spaces: In AngularJS 1.0.3 ng-model on input automatically trims string, so it does not detect that model was changed if you add spaces at the end or at start (so spaces are not automatically removed by my code). But in 1.1.1 there is 'ng-trim' directive that allows to disable this functionality (commit). So I've decided to use 1.1.1 to achieve exact functionality you described in your question.
Solution 2
I believe that the intention of AngularJS inputs and the ngModel
direcive is that invalid input should never end up in the model. The model should always be valid. The problem with having invalid model is that we might have watchers that fire and take (inappropriate) actions based on invalid model.
As I see it, the proper solution here is to plug into the $parsers
pipeline and make sure that invalid input doesn't make it into the model. I'm not sure how did you try to approach things or what exactly didn't work for you with $parsers
but here is a simple directive that solves your problem (or at least my understanding of the problem):
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;
});
}
};
});
As soon as the above directive is declared it can be used like so:
<input ng-model="sth" ng-trim="false" custom-validation>
As in solution proposed by @Valentyn Shybanov we need to use the ng-trim
directive if we want to disallow spaces at the beginning / end of the input.
The advantage of this approach is 2-fold:
- Invalid value is not propagated to the model
- Using a directive it is easy to add this custom validation to any input without duplicating watchers over and over again
Solution 3
A solution to this problem could be to apply the filters on controller side :
$scope.tags = $filter('lowercase')($scope.tags);
Don't forget to declare $filter
as dependency.
Solution 4
If you are using read only input field, you can use ng-value with filter.
for example:
ng-value="price | number:8"
Solution 5
Use a directive which adds to both the $formatters and $parsers collections to ensure that the transformation is performed in both directions.
See this other answer for more details including a link to jsfiddle.
Andrew WC Brown
I'm from Thunder Bay but I've lived a few places so you may have encountered me in Barcelona, Paris, Amsterdam, Toronto, London or Halifax. I'm a full-stack developer of 10+ years.
Updated on February 17, 2022Comments
-
Andrew WC Brown about 2 years
I have a text input and I don't want to allow users to use spaces, and everything typed will be turned into lowercase.
I know I'm not allowed to use filters on ng-model eg.
ng-model='tags | lowercase | no_spaces'
I looked at creating my own directive but adding functions to
$parsers
and$formatters
didn't update the input, only other elements that hadng-model
on it.How can I change the input of that I'm currently typing in?
I'm essentially trying to create the 'tags' feature that works just like the one here on StackOverflow.
-
Andrew WC Brown about 11 yearsThis was exactly what I looking for. It turns out I'm already using angularjs 1.1.1
-
Mark Rajcok about 11 years@Valentyn, your solution applied to SO question I referenced in the comment above. Thanks. stackoverflow.com/questions/12176925/…
-
Valentyn Shybanov about 11 yearsI am sure that tricky part was with
modelCtrl.$setViewValue(transformedInput); modelCtrl.$render();
Useful would be link to documentation: docs.angularjs.org/api/ng.directive:ngModel.NgModelController One word to "protect" my solition is that scope property could be changed not only from views and my way cover this. So I think it depends on a actual situation how scope could be modified. -
GSto almost 11 yearswhat does 'modelCtrl' refer to in your example?
-
Dofs almost 11 yearsWhere do you get the inputValue from?
-
Vignesh PT over 10 yearsThis is really awesome! Worked for me. Thanks
-
Mikhail about 10 yearsThe best solution ever!
-
Nate-Wilkins about 10 years@GSto
modelCtrl
is the controller required by the directive. (require 'ngModel'
) -
Nate-Wilkins about 10 years@Dofs The input value is passed in when the model value changes - see github.com/angular/angular.js/blob/master/src/ng/directive/…
-
Mr Mikkél almost 10 yearsBut you'd need a $watch on it if you want it to update properly.
-
holmesal over 9 yearsNice! This should be the accepted answer - keeping invalid data out the model is far better than watching for changes and retroactively updating. Great answer.
-
pilavdzice over 9 yearsthis solution can have bad side effects, see other answer below, you should use a directive for this
-
BorisOkunskiy over 9 yearsReassigning scope variable from within
$watch
forces the listener to be invoked again. In simple cases (where your filter is idempotent) you will end up with the filter executing twice on every modification. -
Hafez Divandari about 9 yearsCursor jumps to the end of the textfield each time you type an invalid character, try to write 'world' and modify it to 'HeLLo world'!
-
Geuis almost 9 yearsSorry to add to a 2+ year answer but you can also set attrs.ngTrim = 'false' in the link() function and thus avoid needing to add the attribute to the element. Seems it has to be a string, not the boolean value.
-
icfantv about 8 yearsthis is only executed once. and adding to a watch is not the right solution because it, even initially, allows the model to become invalid - the correct solution is to add to the model's $parsers.
-
icfantv about 8 yearsYou don't have to like my answer, but that doesn't mean it's wrong. Check your ego before you downvote.
-
Jens Gabe over 6 yearsThat is exactly what the built in ngList directive does
-
DeLac about 6 yearsThank you. Is it possibile to have a similar thing, without modify the model? I mean, i'd like to use the filter to show the input number as currency, without modify the model into a string
-
ksadowski over 3 years+1 This may not be an answer to the exact question that was asked, but including it is helpful to others whose situation could be slightly different.
-
feltspar about 2 yearsso does this work as we type in the input? Or do we need to add a watcher in the directive?
-
feltspar about 2 yearsignore my last comment. Just learned formatters and parsers are pipeline functions which execute when ng-model changes. The solution presented works for me using $formatters. Including solution as one of the answers. Letting the previous comment be for some lost dev like me.
-
feltspar about 2 yearsHow do we write a unit test for this?