2

I have implemented a date picker with validations in my Angular App.

HTML :

<div class="control-group">
                <label class="control-label" for="reservation.reservedFrom">Reserved From<sup>*</sup></label>
                <div class="controls input-group date" data-provide="datepicker">
                    <!--<input type="text" class="span4" style="width:150px" name="reservedFrom" placeholder="Reserved From" ng-model="reservation.reservedFrom"  ng-change='checkErr()'
                           validator="required" required-error-message="Date is required"  id="startDate122" />-->
                    <input class="form-control" type="text" name="startDate" core-date-picker ng-model="reservation.reservedFrom" >
                    <div class="input-group-addon">
                        <span class="glyphicon glyphicon-th"></span>
                    </div>

                    <span style="color:red">{{errMessageFrom}}</span>

                </div> <!-- /controls -->
            </div> <!-- /control-group -->
            <div class="control-group">
                <label class="control-label" for="reservation.reservedTill">Reserved Till<sup>*</sup></label>
                <div class="controls input-group date" data-provide="datepicker">
                    <!--<input type="text" style="width:150px" class="span4" name="reservedTill" placeholder="Reserved Till" data-ng-model="reservation.reservedTill"
                           validator="required" required-error-message="Date is required" valid-method="watch" id="endDate" ng-change='checkErr()' />-->
                    <input class="form-control" type="text" name="EndDate" core-date-picker ng-model="reservation.reservedTill"  ng-change='checkErr()' id="endDate1">
                    <div class="input-group-addon">
                        <span class="glyphicon glyphicon-th"></span>
                    </div>
                    <span  style="color:red">{{errMessageTo}}</span>

                </div> <!-- /controls -->
            </div> <!-- /control-group -->

Controller :

myApp.controller('editReservationController', ['$scope', '$filter', 'reservationResolved', 'pocResolved', 'accountResolved', 'reservationServices', '$location', '$state',
    function ($scope, $filter, reservationResolved, pocResolved, accountResolved, reservationServices, $location, $state) {
        $scope.reservation = new Object();
        $scope.accounts = accountResolved.data;
        $scope.pocs = pocResolved.data;
        $scope.reservation.employee = reservationResolved.data;





        $scope.updateReservation = function () {
            var from = $scope.reservation.reservedFrom;
            var till = $scope.reservation.reservedTill;
            if ($scope.editReservationForm.$valid && $scope.checkErr()) {

                var from = $scope.reservation.reservedFrom;
                var till = $scope.reservation.reservedTill;

                //var t = $scope.checkErr();

                //TODO: fix it properly
                $scope.reservation.reservedTill = '';
                $scope.reservation.reservedFrom = '';

                $scope.reservation.reservedFrom = $('#startDate').val();
                $scope.reservation.reservedTill = $('#endDate').val();

               reservationServices.updateReservation($scope.reservation).then(function (result) {
                        $scope.data = result.data;
                        if (!result.data.error) {
                            $state.transitionTo('employeeTalentPool', {
                                id: $state.params.id
                            });
                        }
                    });   
            }
        };
        $scope.cancel = function () {
            $location.path("/reservations");
        };

        $scope.$watch("reservation.reservedFrom", function (newValue, oldValue) {
            $scope.checkErr();

        });


        $scope.$watch("reservation.reservedTill", function (newValue, oldValue) {
            $scope.checkErr();

        });
        $scope.getCurrentDate =  function() {
            var utcDate = new Date();
            var isoExtendedDate = utcDate.toISOString();
            var isoSimpleDate = isoExtendedDate.split("T")[0];
            return isoSimpleDate;
        }
        $scope.checkErr = function () {

            var startDateStr = $scope.reservation.reservedFrom;
            var endDateStr = $scope.reservation.reservedTill;

            var startDate;
            if (startDateStr != undefined) {
                startDate = new Date(startDateStr);
            }

            var endDate;
            if (endDateStr != undefined) {
                endDate = new Date(endDateStr);
            }
            $scope.errMessageFrom = '';
            $scope.errMessageTo = '';

            var myDate = new Date(); // Set this to your date in whichever timezone.
            var utcDate = myDate.toUTCString();




            if (startDate != undefined && startDate < new Date()) {
                $scope.errMessageFrom = 'Start Date should be greater than or equal to present day.';
                return false;
            }
            if (endDate != undefined && new Date(endDate) < new Date()) {
                $scope.errMessageTo = 'End Date should be greater than or equal to present day.';
                return false;
            }
            if (startDate != undefined && endDate != undefined  && endDate <= startDate) {
                $scope.errMessageTo = "End date must be after start day.";
                return false;
            }
            return true;
        };
    }]);

App.js :

//Date Picker
myApp.directive('coreDatePicker', function ($compile) {
    return {
        restrict: 'A',
        require: 'ngModel',
        compile: function (element, attrs) {
            $(element[0]).datepicker({
                autoclose: true,
                format: "dd/mm/yyyy"
            });

            return this.link;
        },
        link: function (scope, element, attrs, ngModelCtrl) {
            $(element[0]).datepicker().on("change", function (e) {
                scope.$apply(function () {
                    ngModelCtrl.$setViewValue(element.val());
                });
            });
        }
    };
});

I will explain the scenario I am talking about.

Issue 1 - I selected a date from the calendar and it was displayed correctly in the date-picker text box. I then clicked on the date picker again to select a different date but then I decided not to change the currently selected date, so I clicked outside the calendar without selecting a date for the second time. Since I did not select a date the second time, the date picker cleared the firstly selected date from the text box making it a null. I don't want that to happen. I want the firstly selected date to remain there unless a different date is selected (not a null value).

Issue 2 - While validating the dates, I am having trouble with comparing the selected date with the current date. If the selected date is lesser than current date, the error message should be displayed. But in my case the error is getting displayed if I select the same date as today. It should allow me to select the current day.

I have used Bootstrap-datepicker. Please let me know if more information is required.

Thanks in advance.

Phoenix
  • 285
  • 9
  • 28
  • A [Fiddle](https://jsfiddle.net/) or [Plunkr](https://plnkr.co/) would be much helpful. – Nishant123 Oct 20 '16 at 06:35
  • I dont know how to create a Plunkr or Fiddle with linking the controller and directive etc... sorry – Phoenix Oct 20 '16 at 06:40
  • `core-date-picker` is this your custom directive for datepicker ? I dont see in your code that you are using UI-bootstrap datepicker. – ngCoder Oct 22 '16 at 05:49
  • I am using bootstrap datepicker. Not sure what the difference with UI Bootstrap datepicker is. I got this application as it is. Do you think I should use UI date picker instead? I triedd removing 'core-date-picker' from the html tag and the validations became non functional – Phoenix Oct 24 '16 at 05:09

1 Answers1

1

At a quick glance i noticed ,

 link: function (scope, element, attrs, ngModelCtrl) {
            $(element[0]).datepicker().on("change", function (e) {
                scope.$apply(function () {
                    ngModelCtrl.$setViewValue(element.val());
                });
            });
        }

Should fire onchange right? What i understand from Issue 1 is that it fires regardless if there is change or not. Have you thought of trying some checkings at

$(element[0]).datepicker().on("change", function (e) {
                    scope.$apply(function () {
                        ngModelCtrl.$setViewValue(element.val());
                    });
                });

Try

$(element[0]).datepicker().on("change", function (e) {
                    scope.$apply(function () {
                        if(element.val()) {
                          ngModelCtrl.$setViewValue(element.val());
                       }
                    });
                });

Just a little check on the element.val() for a try wouldn't harm.

For issue two, I don't think you can compare the date that way.

new Date()

returns

Thu Oct 20 2016 14:54:17 GMT+0800 (Malay Peninsula Standard Time)

What is your startDateStr

Noticed new Date() has some seconds and milliseconds.

If you just want to compare days , month and years that is (DD/MM/yyyy) i suggest you convert your dates before you can compare.

Or compare individually like

startDate.getDate() < new Date().getDate() // To compare a day 

FOr a better option iw ould suggest moment. Github.

var startDate = moment(startDateStr, 'MM-DD-YYYY').format('MMMM D');

var today = moment().format('D MMM, YYYY');
if (startDate != undefined && startDate < today) {

}

Update

Have you tried moving the checking to

 $scope.$watch("reservation.reservedFrom", function (newValue, oldValue) {
         if(newValue && newValue !==  oldValue) {
             $scope.checkErr();
         }

        });


        $scope.$watch("reservation.reservedTill", function (newValue, oldValue) {
            if(newValue && newValue !==  oldValue) {
               $scope.checkErr();
            }

        });

When you use <= or >= to compare two date objects, they are compared via valueOf, which is the same as getTime for Date.

But when you use ==, they are two different objects of the same type, so it returns false.

So in your case ^To compare the two dates. The correct way is

 var startDate = moment(startDateStr, 'MM-DD-YYYY').format('MMMM D');

    var today = moment().format('D MMM, YYYY');
    if (startDate != undefined && startDate.getTime() < today.getTime()) {

    }

These explanation is taken Here . You can check the link for more explanation.

The getTime convert the date into an epoch equivalent which is basically numerical/numbers values which can be comapred. new Date() is an object and so is new Date('10/20/2016') and object A cannot be equal to object b. To understand this more you can try on your browser console.

new Date() === new Date();
> false

but

new Date().getTime() === new Date().getTime()
>true;

The first is false is false because you are comparing two objects. The second is equivalent to comparing to numeric values. Afterall

new Date().getTime()
>1477011127232
Community
  • 1
  • 1
Nuru Salihu
  • 4,756
  • 17
  • 65
  • 116
  • Please check the attached screenshot : It will show my date format. https://s10.postimg.org/a9m9cjuqx/screenshot.jpg – Phoenix Oct 20 '16 at 07:48
  • also i tried scope.$apply(function () { if (element.val()) { ngModelCtrl.$setViewValue(element.val()); } It doesn't make any difference. – Phoenix Oct 20 '16 at 07:55
  • @Phoenix like i said, Its clear from your screen shot also. `startDateStr` is `10/21/2016` and you create `startDate` using `new Date(startDate)` . Therefore `startDate` would not have Hours:Minute and seconds . That's why The `HH:MM:SS` for startDate are `00:00:00`. However, `new Date()` would create the current date with hours , minute and seconds hence . `startDate` and new Date() cannot compare unless you take out the seconds, minutes and hours from `new Date()` – Nuru Salihu Oct 20 '16 at 07:57
  • Okay i will take care of the validation using moment as you suggested. How about the date getting cleared after second click? – Phoenix Oct 20 '16 at 08:01
  • Still the same :( https://s12.postimg.org/awc8jhvsd/screenshot.jpg Please check the screenshot. – Phoenix Oct 20 '16 at 08:33
  • @Phoenix you need to convert the two dates to epoch equivalent which is second equivalent. See my update above. – Nuru Salihu Oct 21 '16 at 00:33