Using angularjs filter in input element
Solution 1
In short: if you want your data to have a different representation in the view and in the model, you will need a directive, which you can think of as a two-way filter.
Your directive would look something like
angular.module('myApp').directive('myDirective', function() {
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModelController) {
ngModelController.$parsers.push(function(data) {
//convert data from view format to model format
return data; //converted
});
ngModelController.$formatters.push(function(data) {
//convert data from model format to view format
return data; //converted
});
}
}
});
HTML:
<input my-directive type="text" data-ng-model="entity.date" />
Here is a working jsFiddle example.
Solution 2
Having different values in your input field and in your model goes against the very nature of ng-model. So I suggest you take the simplest approach and apply your filter inside the controller, using a separate variable for formatted date, and employing watchers to keep formatted and original dates in sync:
HTML:
<input ui-datetime type="text" data-ng-model="formattedDate" />
JS:
app.controller('AppController', function($scope, $filter){
$scope.$watch('entity.date', function(unformattedDate){
$scope.formattedDate = $filter('date')(unformattedDate, 'dd/MM/yyyy HH:mm:ss a');
});
$scope.$watch('formattedDate', function(formattedDate){
$scope.entity.date = $filter('date')(formattedDate, 'yyy/MM/dd');
});
$scope.entity = {date: '2012/12/28'};
});
Solution 3
If your input only displays data
If you actually need an input to simply display some information and it is some other element that changes the Angular model you can make an easier change.
Instead of writing new directive simply DO NOT USE the ng-model
and use good, old value
.
So instead of:
<input data-ng-model={{entity.date|date:'dd/MM/yyyy HH:mm:ss'}}" />
This will do:
<input value="{{entity.date|date:'dd/MM/yyyy HH:mm:ss'}}" />
And works like a charm :)
Solution 4
Complete example that formats numbers, inserting spaces every 3 characters, starting from the end:
'use strict'
String::reverse = ->
@split('').reverse().join('')
app = angular.module('app', [])
app.directive 'intersperse', ->
require: 'ngModel'
link: (scope, element, attrs, modelCtrl) ->
modelCtrl.$formatters.push (input) ->
return unless input?
input = input.toString()
input.reverse().replace(/(.{3})/g, '$1 ').reverse()
modelCtrl.$parsers.push (input) ->
return unless input?
input.replace(/\s/g, '')
Usage:
<input ng-model="price" intersperse/>
Plunkr example: http://plnkr.co/edit/qo0h9z
leon.io
Updated on July 05, 2022Comments
-
leon.io about 2 years
I hope I haven't missed anything obvious in the doco, if I have I'm sure someone will help.
I'm using asp.net webapi to return a DTO, with date fields. These are serialized using JSON.Net (in format '2013-03-11T12:37:38.693').
I'd like to use a filter but in an INPUT element, is this possible or should I create a new filter or directive to accomplish this?
// this just displays the text value <input ui-datetime type="text" data-ng-model="entity.date" /> // this doesn't work at all <input ui-datetime type="text" data-ng-model="{{entity.date|date:'dd/MM/yyyy HH:mm:ss a'}}" /> // this works fine {{entity.date|date:'dd/MM/yyyy HH:mm:ss a'}}
Is there any shortcut I'm missing?
-
holographic-principle over 11 yearsI believe creating a new directive to fulfill the requirements is a cleaner solution here. If I understood the OP's problem correctly.
-
Stewie over 11 yearsYou're right. It is cleaner. I just wanted to give @leon an option, in case he/she is not so comfortable with custom directives yet.
-
leon.io over 11 yearsThanks guys, both answers appreciated as it's great to learn alternative methods.
-
Paul Taylor almost 11 yearsLooks good, but how would you apply it? Specifically, how to specify a filter to use in formatting?
-
Valentin V almost 11 years-1, two variable + watching is a support hell. Imagine a scope with dozens of variables (insurance calculators etc)
-
Valentin V almost 11 years-1, OP was asking about the specific problem of using a filter with
ngModel
, which is drastically different from creating a new filter. -
Vincent almost 11 yearsWhat exactly does modelCtrl.$parsers(or formatters).push does?
-
Valentin V almost 11 years@Vincent, it adds a function to the array of formatters that is invoked when value from model needs to be displayed. Think of of $formatters array as a pipeline filled with functions where each function is called and the result is passed further through the pipeline. Once all functions are called, the final result is displayed. The $parsers is a similar thing but for writing to model.
-
mattcole almost 11 yearsI don't think this actually works, do you have a plunker or similar?
-
Aditya M P over 10 years@PaulTaylor restrict this directive to 'attribute' only, and use that attribute on an input field (similar to how you write
<input ng-model="foo">
. -
Aditya M P over 10 yearsAlso check out this answer for a fuller example.
-
ViniciusPires about 10 yearsWorking example on JSFiddle?
-
ViniciusPires about 10 yearsNice! The only problem is that it adds a space before the number in any length multiple of 3, and the right approach would be adding the space only when the length is multiple of 3 AND has another number after it. It doesn't look like a problem when your locale doesn't define the number separator as a dot (
.
), it looks like this:.999.999
-
ViniciusPires about 10 yearsThanks! The only thing your example was lacking for my solution was watching the model for changes and re-running the formatter. I didn't understand how could I achieve that easily with AngularJS, I had to force the element update:
scope.$watch(attrs.ngModel, function(value){ element.val( $filter('number')(value, 0) ); });
-
ViniciusPires about 10 yearsPS.: Working with
$filter('number');
seemed to be a better way, it loads the format through the$locale
service, and you can usenew RegExp("\\"+$locale.NUMBER_FORMATS.GROUP_SEP, 'g');
to reverse the value to the model... -
holographic-principle about 10 yearsYou should not have to do that. Just defining the formatter function should be enough, unless you are updating your model from outside the scope of angular, in which case you should trigger
$scope.$apply()
-
jao almost 10 yearsthis is only half of the needed code, for example, where should I put this return function?
-
Vidul almost 9 years@finishingmove Great answer!