107

I posted the same question in different form, but no one answered. I am not getting a clear picture of what the Formatters and Parsers do in angular js.

By the definition, both the Formatters and Parsers look similar to me. Maybe I am wrong, as I am new to this angularjs.

Formatters Definition

Array of functions to execute, as a pipeline, whenever the model value changes. Each function is called, in turn, passing the value through to the next. Used to format / convert values for display in the control and validation.

Parsers Definition

Array of functions to execute, as a pipeline, whenever the control reads value from the DOM. Each function is called, in turn, passing the value through to the next. Used to sanitize / convert the value as well as validation. For validation, the parsers should update the validity state using $setValidity(), and return undefined for invalid values.

Please help me to understand both features with a simple example. A simple illustration of both will be appreciated.

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
RONE
  • 5,415
  • 9
  • 42
  • 71
  • 2
    Formatters modify the displayed value of a model, like displaying `(123) 123-1234` for a phone number. Parsers read the data each time it changes and typically are used to set the $valid state of the input. The docs have examples of both. – km6zla Apr 03 '14 at 14:54

2 Answers2

159

This topic was covered really well in a related question: How to do two-way filtering in AngularJS?

To summarize:

  • Formatters change how model values will appear in the view.
  • Parsers change how view values will be saved in the model.

Here is a simple example, building on an example in the [NgModelController api documentation][1]:

  //format text going to user (model to view)
  ngModel.$formatters.push(function(value) {
    return value.toUpperCase();
  });
  
  //format text from the user (view to model)
  ngModel.$parsers.push(function(value) {
    return value.toLowerCase();
  });

You can see it in action: https://plnkr.co/plunk/UQ5q5FxyBzIeEjRYYVGX

<input type="button" value="set to 'misko'" ng-click="data.name='misko'"/>
<input type="button" value="set to 'MISKO'" ng-click="data.name='MISKO'"/>
<input changecase ng-model="data.name" />

When you type a name in (view to model), you will see that the model is always lowercase. But, when you click a button and programatically change the name (model to view), the input field is always uppercase. [1]: http://docs.angularjs.org/api/ng/type/ngModel.NgModelController

j.wittwer
  • 9,497
  • 3
  • 30
  • 32
  • 2
    is there any way to set this change as the user types? You say "programatically", but I am trying to get the $viewValue to be formatted as the user enters into the input, for example for credit card number formatting – iamyojimbo Mar 07 '15 at 19:06
  • 3
    @SavvasNicholas If I'm not mistaken, you'd use `ngModel.$setViewValue(transformedInput);` to set it and `ngModel.$render();` to render it from the $parsers function. – Jacob Ensor Aug 06 '15 at 14:51
  • 1
    In my case, what `$formatters` do, is immediately reverted by `$validators`. ;( – Mikhail Batcer Nov 12 '15 at 11:18
  • I'm a bit confused - what needs to change if this is an element directive, not an attribute one? – George Mauer Feb 16 '16 at 20:35
  • Hi, I have formatter and parser around an input element. Example: My view gets changed to credit card structure (XXXX-XXXX-XXXX-XXXX) with hyphens and also it has to work in the same way if model doesn't have hyphens in it. My issues is, I want to use this viewValue(with proper hyphenation) in another text field. any suggestion on how to do it plz? – Sreekanth Aug 03 '16 at 16:14
  • 1
    FYI the referenced plunkr does not exist any more – Chris Brown Jun 20 '18 at 08:15
  • 1
    I notice the formatter only works if you push the button, not if you type the name into the field – nuander Nov 08 '18 at 17:36
  • The above plunker link is not working... – Common Man Apr 07 '23 at 12:32
  • 1
    @CommonMan, I have updated the plunker link – j.wittwer Apr 14 '23 at 17:26
7

Another usage for formatters and parsers is when you want to store dates in UTC time and display them in local time on inputs, I created the below datepicker directive and utcToLocal filter for this.

(function () {
    'use strict';

    angular
        .module('app')
        .directive('datepicker', Directive);

    function Directive($filter) {
        return {
            require: 'ngModel',
            link: function (scope, element, attr, ngModel) {
                element.addClass('datepicker');
                element.pickadate({ format: 'dd/mm/yyyy', editable: true });

                // convert utc date to local for display
                ngModel.$formatters.push(function (utcDate) {
                    if (!utcDate)
                        return;

                    return $filter('utcToLocal')(utcDate, 'dd/MM/yyyy');
                });

                // convert local date to utc for storage
                ngModel.$parsers.push(function (localDate) {
                    if (!localDate)
                        return;

                    return moment(localDate, 'DD/MM/YYYY').utc().toISOString();
                });
            }
        };
    }
})();

It uses this utcToLocal filter that ensures the input date is in the correct format before converting to local time.

(function () {
    'use strict';

    angular
        .module('app')
        .filter('utcToLocal', Filter);

    function Filter($filter) {
        return function (utcDateString, format) {
            if (!utcDateString) {
                return;
            }

            // append 'Z' to the date string to indicate UTC time if the timezone isn't already specified
            if (utcDateString.indexOf('Z') === -1 && utcDateString.indexOf('+') === -1) {
                utcDateString += 'Z';
            }

            return $filter('date')(utcDateString, format);
        };
    }
})();

moment.js is used to convert local to utc dates.

pickadate.js is the datepicker plugin used

Jason Watmore
  • 4,521
  • 2
  • 32
  • 36