Using angularjs filter in input element

91,221

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

Share:
91,221
leon.io
Author by

leon.io

Updated on July 05, 2022

Comments

  • leon.io
    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
    holographic-principle over 11 years
    I believe creating a new directive to fulfill the requirements is a cleaner solution here. If I understood the OP's problem correctly.
  • Stewie
    Stewie over 11 years
    You'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
    leon.io over 11 years
    Thanks guys, both answers appreciated as it's great to learn alternative methods.
  • Paul Taylor
    Paul Taylor almost 11 years
    Looks good, but how would you apply it? Specifically, how to specify a filter to use in formatting?
  • Valentin V
    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
    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
    Vincent almost 11 years
    What exactly does modelCtrl.$parsers(or formatters).push does?
  • Valentin V
    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
    mattcole almost 11 years
    I don't think this actually works, do you have a plunker or similar?
  • Aditya M P
    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
    Aditya M P over 10 years
    Also check out this answer for a fuller example.
  • ViniciusPires
    ViniciusPires about 10 years
    Working example on JSFiddle?
  • ViniciusPires
    ViniciusPires about 10 years
    Nice! 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
    ViniciusPires about 10 years
    Thanks! 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
    ViniciusPires about 10 years
    PS.: Working with $filter('number'); seemed to be a better way, it loads the format through the $locale service, and you can use new RegExp("\\"+$locale.NUMBER_FORMATS.GROUP_SEP, 'g'); to reverse the value to the model...
  • holographic-principle
    holographic-principle about 10 years
    You 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
    jao almost 10 years
    this is only half of the needed code, for example, where should I put this return function?
  • Vidul
    Vidul almost 9 years
    @finishingmove Great answer!