Monday, July 27, 2015

AngularJS $formatters

Suppose you have a new directive that declares a tag like:

<input type="array">

In AngularJS v1.4.1, when you add an "input" directive, AngularJS decides how its going to store your data.  Will your data be stored as a string (in a text node), in a JavaScript object (hanging off a DOM node) or what?

In the case above, is the data stored as a string like "[1, 2, 3, 4]" in a text node or is it stored as a JavaScript array like ctrl.array = [1, 2, 3, 4]?

It decides by running through a whole list of known (to AngularJS) input types and, if it does not know about a new, unexpected input type, it decides that it must be stored as a string.

The known types are:
  • text
  • date ('yyyy-MM-dd')
  • datetime-local ('yyyy-MM-ddTHH:mm:ss.sss')
  • time ('HH:mm:ss.sss')
  • week ('yyyy-Www')
  • month ('yyyy-MM')
  • number
  • url
  • email
  • radio
  • checkbox
  • hidden
  • button
  • submit
  • reset
  • file
If you default to being stored as a string, it adds this function to the $formatters object on the caller's instantiated directive.

function stringBasedInputType(ctrl) {
  ctrl.$formatters.push(function(value) {
    return ctrl.$isEmpty(value) ? value : value.toString();

As you can see, if the object doesn't have a toString() method, it will call the default toString() method for all JavaScript objects.  If that happens, the user's data may be "stored" as "[object Object]".  Oh, no!

To avoid this, make sure that you empty or change the $formatters array so it is appropriate:

  .directive('input', function(...) {
    return {
      restrict: 'E',
      require: '?ngModel',
      link: function($scope, $element, $attributes, ngModel) {
        ngModel.$formatters = [];

That way, you can get the unformatted object and handle it appropriately.

No comments:

Post a Comment