0
votes

I'm trying to get input from text boxes to bind to scope variables as actual JavaScript Date objects, not strings. The inputs are generated dynamically so I'm unable to cast/convert before the values are sent to the server.

So far, I have created a directive that uses moment.js to parse the value on the scope, and convert it to a Date() object. The problem seems to be that angular converts the value back to a string immediately after. I guess it rechecks the html input and overwrites the Date() object set in the directive.

Here is a working Plunkr demonstrating the issue

(function () {

    'use strict';

    angular.module('app', ['ng'])

    .controller('myController', ['$scope', function() {

        $scope.testObj = null;

    }]) 

    .directive('dateBinding', function () {

        return {

            restrict: 'A',
            require: 'ngModel',
            scope: false,
            link: function (scope, element, attrs, ngModel) {

                var parseFormat = attrs.dateBinding;

                scope.$watch(

                    function() { 

                      console.log('watching model', ngModel.$modelValue);

                      return ngModel.$modelValue;

                    }, 
                    function (val) {

              console.log('recieved model', val);

              if (val && typeof val == 'string') {

                console.log('attempting parse date', val);

                if(moment(val, parseFormat).isValid())
                {
                  console.log('string is valid date');

                  ngModel.$modelValue = moment(val, parseFormat).toDate();

                  console.log('completed value assignment', ngModel.$modelValue);

                  console.log('model is of type ' + typeof ngModel.$modelValue);

                  console.log('model is date', (ngModel.$modelValue instanceof Date));
                }
                else
                {
                  console.log('string is not a valid date');
                }
              }
                    }
                );
            }
        };
    })

} ());

You can see the behaviour by opening the console in a browser while running the plunkr. The line 'completed value assignment' shows that at least momentarily, ngModel.$modelValue (from $scope.testObj) is a Date() object.

The final line in the output below shows the watch firing again, and the model value is once again a string as it appears in the html input.

enter image description here

How can I have the value persist as a Date object (once a valid date can be parsed).

1

1 Answers

2
votes

You have to use the $parsers and $formatters pipelines, described in the docs of ngModelController. The code would be:

.directive('dateBinding', function () {

    return {

        restrict: 'A',
        require: 'ngModel',
        scope: false,
        link: function (scope, element, attrs, ngModel) {

            var parseFormat = attrs.dateBinding;

            function parse(value) {
              var m = moment(value, parseFormat);
              if( m && m.isValid() ) {
                  ngModel.$setValidity('dateBinding', true);
                return m.toDate();
              }
              else {
                  ngModel.$setValidity('dateBinding', false);
                return; // undefined
              }
            }

            function format(value) {
              if( value && value instanceof Date ) {
                return moment(d).format(parseFormat);
              }
              else {
                return '';
              }
            }

            ngModel.$formatters.push(format);
            ngModel.$parsers.unshift(parse);
        }
    };
});

See (and play with) the forked plunk: http://plnkr.co/edit/VboH2iq6HRlaDhX3g1AY?p=preview