Model is not a date object on input in AngularJS

All we need is an easy explanation of the problem, so here it is.

Using AngularJS I am trying to display a date using an input type=date:

<input ng-model="campaign.date_start" type="date">

However, this gives the following error:

Error: error:datefmt
Model is not a date object

The date actually comes from a JSON API in the following format:

date_start": "2014-11-19"

I thought that I could resolve it by using a filter, but this did not work and I get the same error with:

 <input ng-model="campaign.date_start | date" type="date">

I have also tried converting the string to a date, but again I get the same error:

 $scope.campaign.date_start = Date(campaign.date_start);

What else can I try?

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

You can use this directive;

angular.module('app')
.directive("formatDate", function(){
  return {
   require: 'ngModel',
    link: function(scope, elem, attr, modelCtrl) {
      modelCtrl.$formatters.push(function(modelValue){
        return new Date(modelValue);
      })
    }
  }
})

In your html;

<input type="date" ng-model="date" format-date>

$formatters

Array.<Function>

Array of functions to execute, as a pipeline, whenever the model value changes. The functions are called in reverse array order, each passing the value through to the next. The last return value is used as the actual DOM value. Used to format / convert values for display in the control.

Method 2

You have to instantiate campaign.date_start with Date not use it as a function.

It should look something like this (small demo):

$scope.campaign.date_start = new Date(campaign.date_start);

Method 3

cs1707‘s directive is a great, except if the scope value for the date is null or undefined then it will initialize a date with 1/1/1970. This is probably not optimal for most people

Below is a modified version on cs1707‘s directive that will leave a null/undefined model as is:

angular.module('app').directive("formatDate", function() {
    return {
        require: 'ngModel',
        link: function(scope, elem, attr, modelCtrl) {
            modelCtrl.$formatters.push(function(modelValue) {
                if (modelValue){
                    return new Date(modelValue);
                }
                else {
                    return null;
                }
            });
        }
    };
});

In your html;

<input type="date" ng-model="date" format-date>

Another Option

If you want this to apply to all inputs of type date, then there is no need to add the format-date attribute to each input element. You can use the following directive. (Be careful with this as it might interact with other custom directives in unexpected ways.)

angular.module('app').directive("input", function() {
    return {
        require: 'ngModel',
        link: function(scope, elem, attr, modelCtrl) {
            if (attr['type'] === 'date'){
                modelCtrl.$formatters.push(function(modelValue) {
                    if (modelValue){
                        return new Date(modelValue);
                    }
                    else {
                        return null;
                    }
                });
            }

        }
    };
});

In your html;

<input type="date" ng-model="date">

Method 4

Another directive solution here:

//inside directives.js
.directive('dateField', function () {
    return {
        restrict: ' E',
        scope: {
            ngBind: '=ngModel',
            ngLabel: '@'
        },
        replace: true,
        require: 'ngModel',
        controller: function ($scope) {
            if ($scope.ngBind != null) {
                var pattern = /Date\(([^)]+)\)/;
                var results = pattern.exec($scope.ngBind);
                var dt = new Date(parseFloat(results[1]));
                $scope.ngBind = dt;
            };
        },
        templateUrl: 'templates/directives/dateField.html'
    }
})
;

Add a directive template like this:

<!-- app.directives templates/directives/dateField -->
<script id="templates/directives/dateField.html" type="text/ng-template">    
    <label class="item item-input item-stacked-label ">
        <span class="input-label">{{ngLabel}}</span>
        <input type="date" placeholder="" ng-model="ngBind" />
    </label>
</script>

And use it

<date-field ng-label="This date..." ng-model="datajson.do.date"></date-field>

Good luck!

Method 5

Using directive to reset default angular formatters/parsers by ngModelCtrl.$formatters.length = 0; ngModelCtrl.$parsers.length = 0;

It works for input[type="date"] as well as input[type="time"]. Also works well for cordova app

HTML :

<input date-input type="time" ng-model="created_time">

Angular Directive:

app.directive('dateInput', function () {
    return {
        require: 'ngModel',
        link: function (scope, element, attr, ngModelCtrl) {
            //Angular 1.3 insert a formater that force to set model to date object, otherwise throw exception.
            //Reset default angular formatters/parsers
            ngModelCtrl.$formatters.length = 0;
            ngModelCtrl.$parsers.length = 0;
        }
    };
});

Method 6

Another simple way using a directive:

HTML:

<input date-input type="time" ng-model="created_time">

Directive:

app.directive('dateInput', function(){
    return {
        restrict : 'A',
        scope : {
            ngModel : '='
        },
        link: function (scope) {
            if (scope.ngModel) scope.ngModel = new Date(scope.ngModel);
        }
    }
});

Method 7

You can avoid from this error using this piece of code which is actually a date format error while we pass our date to some function or API

            var options = {
                weekday: "long", year: "numeric", month: "short",
                day: "numeric", hour: "2-digit", minute: "2-digit"
            };
            $scope.campaign.date_start = $scope.campaign.date_start.toLocaleTimeString("en-us", options);

where en-us format = Friday‎, ‎Feb‎ ‎1‎, ‎2013‎ ‎06‎:‎00‎ ‎AM hope this will help others to solve issue, i was facing such error and resolved with this.

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply