synergetic
synergetic

Reputation: 8026

angularjs: parsing date input

I need a directive which will parse user input to date and validate it. So I wrote the following:

myDirectives.directive('myDate', function($filter) {
    'use strict';
    return {
        require:'ngModel',
        restrict:'A',
        link:function (scope, elem, attrs, ctrl) {
            var dateFormat = attrs.myDate ? attrs.myDate : 'shortDate';

            ctrl.$formatters.unshift(function(modelValue) {
                return $filter('date')(modelValue, dateFormat);
            });
            ctrl.$parsers.unshift(function(viewValue) {
                var date = new Date(viewValue);

                if (isNaN(date)) {
                    ctrl.$setValidity('date', false);
                    return undefined;
                } else {
                    var dateString = $filter('date')(date, dateFormat);
                    if (dateString !== viewValue) {
                        ctrl.$setViewValue(dateString);
                    }
                    ctrl.$setValidity('date', true);
                    return date;
                }
            });
        }
    };
});

Parsing need to occur only after when input loses focus, so I use another directive, which I found here. The problem is

ctrl.$setViewValue(dateString);

won't work, because as indicated in angularjs documentation, setViewValue() must be called from within a DOM event handler. What should I do to reflect back parsing result?

Upvotes: 4

Views: 9038

Answers (4)

Giancarlo Paoli
Giancarlo Paoli

Reputation: 1

on html:

<input class="form-control""
type="date"
string-to-date
ng-model="val.VAL">

On Controller create a directive

.directive('stringToDate', function() {
    return {
      require: 'ngModel',
      link: function(scope, element, attrs, ngModel) {
        ngModel.$parsers.push(function(value) {
          return new Date(value);
        });
        ngModel.$formatters.push(function(value) {
          var fechaactual = new Date(value);
          return fechaactual;
        });
      }
    };
  })

note that the directive call stringToDate and you have to call it as string-to-date in the html. Dont not why :)

Upvotes: -1

Dzulqarnain Nasir
Dzulqarnain Nasir

Reputation: 2300

I've created a date parser that converts string to Date objects. You can also provide date formats you're using, as well as your date locale. It returns a Date object, valid or otherwise depending on the input.

There's also a directive that implements this parser so you can use it on an input field, for example.

https://github.com/dnasir/angular-dateParser

Upvotes: 2

TheSharpieOne
TheSharpieOne

Reputation: 25726

This should work:

var setter = $parse('ngModel').assign;
setter($scope,valueToSet);
$scope.$apply() // This may be needed depending on where its done.

Refenece: http://docs.angularjs.org/api/ng.$parse

Upvotes: 0

synergetic
synergetic

Reputation: 8026

Instead of

ctrl.$setViewValue(dateString);

I needed to write

elem.val(dateString);

and the problem was solved. So, my directive now looks like below:

myDirectives.directive('myDate', function ($filter) {
    'use strict';
    return {
        require: 'ngModel',
        restrict: 'A',
        link: function (scope, elem, attrs, ctrl) {
            var dateFormat = attrs.myDate ? attrs.myDate : 'shortDate';
            ctrl.$formatters.unshift(function (modelValue) {
                return (modelValue) ? $filter('date')(modelValue, dateFormat) : '';
            });
            ctrl.$parsers.unshift(function (viewValue) {
                var date = new Date(viewValue);
                if (isNaN(date)) {
                    ctrl.$setValidity('date', false);
                    return undefined;
                } else {
                    var dateString = $filter('date')(date, dateFormat);
                    if (dateString !== viewValue) {
                        elem.val(dateString);
                    }
                    ctrl.$setValidity('date', true);
                    return date;
                }
            });
            elem.unbind('input').unbind('keydown').unbind('change');
            elem.bind('blur', function () {
                scope.$apply(function () {
                    ctrl.$setViewValue(elem.val()); //this method name is misleading; 
                                                    //it actually sets model value?!
                });
            });
        }
    };
});

Note, that I incorporated the code from another directive which was responsible for pushing view value to model, when focus is lost.

Upvotes: 2

Related Questions