Level Extreme platform
Subscription
Corporate profile
Products & Services
Support
Legal
Français
Which version you're using?
Message
 
 
General information
Forum:
Javascript
Category:
Other
Miscellaneous
Thread ID:
01624940
Message ID:
01625096
Views:
40
>>I think it's actually working correctly for me (so I don't know if updating to the latest version did any good).
>>
>>The problem is again our Datetime picker directive. It is making the dirty state and then the normal page flow is disrupted. Once we commented one line in that directive, my pages started to work correctly.
>>
>>We do need this line in the directive, though.
>>
>>My colleague started to switch to another datetime picker control, but we haven't yet decided if we're going to use it or not.
>
>Unsure now of your state of play - you posted this after replies to my last post....
>
>What it the line in the DatePicker directive that causes the problem (and what DatePicker are you using) ?

This is the whole directive:
(function () {
    'use strict';

    var app = angular.module('sysMgrApp');

    app.directive('smDateTimePicker', [function () {
        return {
            require: 'ngModel',
            restrict: 'E',

            scope: {
                ngModel: '=',//the date being saved to the database
                compareDate: '=',//either the start or end date of the range for validation (isDateRange attr must be set to true)
                form: '=',//the name of the form (if a subform, this will be parent.subform), used for setting the form to dirty and validation
                name: '@',//the input's name, used to manually outline invalid fields in red
                placeholder: '@',//text of placeholder on input field, 
                showBod: '=',//if true, we display the Beginning of Day button (pickTime must be true)
                showEod: '=',//if true, we display the End of Day button (pickTime must be true)
                showNow: '=',//if true, we display the Now button
                setBod: '&',//function defined in the directive that is called when clicking on the BOD button
                setEod: '&',//function defined in the directive that is called when clicking on the EOD button
                setNow: '&',//function defined in the directive that is called when clicking on the Now button
                validStartDateRange: '&',//function defined in the directive that determines whether to show validation message
                validEndDateRange: '&',
                validRequired: '&',
                validFutureDate: '&',
                validPastDate: '&'
            },
            templateUrl: 'app/templates/smDateTimePicker',
            link:  function (scope, element, attrs, controller) {
                    if (!controller) return undefined;

                    var updateModel, onblur, datetimepicker, options;
                    var programmaticChange = false;
                    var pickTime = attrs.pickTime === "true" ? true : false;
                    var isDateRange = attrs.isDateRange === "true" ? true : false; //if true, we do range validation on start and end dates
                    var isRequired = attrs.isRequired === "true" ? true : false; //if true, we do required validation
                    scope.showBod = (scope.showBod && pickTime); //only display showBod button if pickTime is true
                    scope.showEod = (scope.showEod && pickTime); //only display showEod button if pickTime is true
                    var onlyFutureDates = attrs.onlyFutureDates === "true" ? true : false;
                    var onlyPastDates = attrs.onlyPastDates === "true" ? true : false;
                    var isEndDate = attrs.isEndDate === "true" ? true : false;
                    var noDirtyCheck = attrs.noDirtyCheck === "true" ? true : false;

                    var format = function (date) {
                        if (date) {
                            if (!pickTime) {
                                if (isEndDate && !onlyFutureDates) {
                                    date = date.hours(23).minutes(59).seconds(59).milliseconds(997);
                                    date = date.format("YYYY-MM-DD HH:mm:ss.SSS").toString();
                                } else {
                                    date = date.hours(0).minutes(0).seconds(0).milliseconds(0);
                                    date = date.format("YYYY-MM-DD HH:mm:ss").toString();
                                }
                            }
                            else
                            {
                                date = date.format("YYYY-MM-DD HH:mm:ss.SSS").toString();
                            }
                        }
                        return date;
                    };

                    var setStartDateLimit = function () {
                        if (onlyFutureDates) {
                            return moment().add('days', 1);
                        }
                        return moment("1753 01 01", "YYYY MM DD");
                    };

                    var setEndDateLimit = function () {
                        if (onlyPastDates) {
                            return moment().subtract('days', 1);
                        }
                        return moment("9999 12 31", "YYYY MM DD");
                    };

                    var setCalendarPickerStartDate = function () {
                        if (onlyFutureDates) {
                            return moment().add('days', 1);
                        } else if (onlyPastDates) {
                            return moment().subtract('days', 1);
                        } else {
                            return moment();
                        }
                    };
                    var init = function () {
                        options = {
                            icons: {
                                time: "fa fa-clock-o icon-1x",
                                date: "fa fa-calendar icon-1x",
                                up: "fa fa-arrow-up",
                                down: "fa fa-arrow-down"
                            },
                            //pickDate: true,
                            //pickTime: pickTime,
                            format: pickTime ? 'MM/DD/YYYY hh:mm:ss a' : 'MM/DD/YYYY',
                            //language: 'en',
                            locale: 'en',
                            //startDate: format(setStartDateLimit()),
                            //endDate: format(setEndDateLimit()),
                            minDate: setStartDateLimit(),
                            maxDate: setEndDateLimit(),
                            useStrict: true
                        };
                        datetimepicker = element.find('.input-group').datetimepicker(options);
                    };

                    init();

                    controller.$render = function () {
                        var viewValue = controller.$viewValue;
                        programmaticChange = true;
                         if (viewValue) {
                            var date = moment(viewValue);
                            if (angular.isDefined(date) && date != null && moment.isMoment(date)) {
// Below is the line causing the control to become dirty and therefore another directive is firing when we're trying to switch to another view and thus causing all sorts of bugs               

                 datetimepicker.data('DateTimePicker').date(date);

                            }
                        } else {
                        //    datetimepicker.data('DateTimePicker').date(setCalendarPickerStartDate());
                        //    datetimepicker.data('DateTimePicker').date();
                        }
                        return controller.$viewValue;
                    };

                    updateModel = function () {
                        //if (datetimepicker.data("DateTimePicker").minViewMode === datetimepicker.data("DateTimePicker").viewMode) {
                        datetimepicker.blur();
                        if (noDirtyCheck)
                            element[0].$dirty = false;
                        //}
                    };

                    onblur = function () {
                        var newValue = datetimepicker.data("DateTimePicker").date();
                        
                        return scope.$evalAsync(function () {
                            if ((!programmaticChange) && (!noDirtyCheck))
                                scope.form.$setDirty();
                            programmaticChange = false;
                            if (newValue) {
                                //return controller.$setViewValue(format(moment(newValue)));
                                return controller.$setViewValue(format(newValue));
                            } else {
                                return controller.$setViewValue();
                            }
                        });
                    };

                    scope.$watch('compareDate', function () {
                        validate();
                    });

                    scope.$watch('ngModel', function () {
                        validate();
                        datetimepicker.on('dp.change', updateModel).on('blur', onblur);
                    });

                    scope.setBod = function () {
                        var viewValue = controller.$viewValue;
                        var bod = format(moment(viewValue).hours(0).minutes(0).seconds(0).milliseconds(0));
                        datetimepicker.data("DateTimePicker").date(bod);
                        scope.form.$setDirty();
                        scope.ngModel = bod;
                    };

                    scope.setEod = function () {
                        var viewValue = controller.$viewValue;
                        var eod = format(moment(viewValue).hours(23).minutes(59).seconds(59).milliseconds(997));
                        datetimepicker.data("DateTimePicker").date(eod);
                        scope.form.$setDirty();
                        scope.ngModel = eod;
                    };

                    scope.setNow = function () {
                        var now = format(moment());
                        datetimepicker.data("DateTimePicker").date(now);
                        scope.form.$setDirty();
                        scope.ngModel = now;
                    };

                    //NOTE: We have to manually set these valid/invalid classes and required validation
                    //because we're not putting ng-model on the input field. 
                    //We're not putting ng-model on the input field
                    //because two-way data-binding would prevent the user 
                    //from being able to type in a date.
                    var validate = function () {
                        if (controller.$dirty || (scope.form && scope.form.$dirty)) {
                            var input = $("#" + scope.name);
                            validateRequired(scope.ngModel);
                            validateFutureDate(input, scope.ngModel);
                            validatePastDate(input, scope.ngModel);
                            validateDateRange(scope.ngModel, scope.compareDate);

                            if (controller.$error.daterange ||
                                controller.$error.required ||
                                controller.$error.futuredate ||
                                controller.$error.pastdate) {
                                setInValid(input);
                            } else {
                                setValid(input);
                            }
                        }
                    };

                    var setValid = function (input) {
                        input.removeClass("ng-invalid");
                        input.addClass("ng-valid");
                    };

                    var setInValid = function (input) {
                        input.removeClass("ng-valid");
                        input.addClass("ng-invalid");
                    };

                    var validateFutureDate = function (input, date) {
                        if (onlyFutureDates) {
                            var inputValue = element.find('input').val();
                            if (!inputValue) {
                                setFutureDateValidity(true);
                            } else if (moment(date) <= moment()) {
                                setFutureDateValidity(false);
                            } else {
                                setFutureDateValidity(true);
                            }
                        }
                    };

                    var validatePastDate = function (input, date) {
                        if (onlyPastDates) {
                            var inputValue = element.find('input').val();
                            if (!inputValue) {
                                setPastDateValidity(true);
                            } else if (moment(date) >= moment()) {
                                setPastDateValidity(false);
                            } else {
                                setPastDateValidity(true);
                            }
                        }
                    };

                    var validateDateRange = function (ngModel, compareDate) {
                        if (ngModel===null || compareDate===null)
                            return ; // We can not compare null with a date

                        if (isDateRange &&
                            !controller.$error.futuredate &&
                            !controller.$error.pastdate) {
                            var inputValue = element.find('input').val();
                            if (!ngModel && !compareDate) {
                                setDateRangeValidity(true);
                            } else if ((!ngModel || !compareDate) &&
                                (ngModel || compareDate) && !inputValue) {//here we need to check the input value if we can
                                setDateRangeValidity(false);
                            } else {
                                var newStartDate;
                                var newEndDate;
                                if (isEndDate) {
                                    newEndDate = moment(ngModel);
                                    newStartDate = moment(compareDate);
                                } else {
                                    newStartDate = moment(ngModel);
                                    newEndDate = moment(compareDate);
                                }
                                if (newStartDate.isBefore(newEndDate) ||
                                    newStartDate.isSame(newEndDate)) {
                                    setDateRangeValidity(true);
                                } else {
                                    setDateRangeValidity(false);
                                }
                            }
                        } else {
                            setDateRangeValidity(true);
                        }
                    };

                    var validateRequired = function (input, date) {
                        if (isRequired) {
                            if (date) {
                                setRequiredValidity(true);
                            } else {
                                setRequiredValidity(false);
                            }
                        }
                    };

                    scope.validStartDateRange = function () {
                        if (isDateRange) {
                            return (!(controller.$error.daterange && (controller.$dirty || scope.form.$dirty) && !isEndDate));
                        }
                        return true;
                    };

                    scope.validEndDateRange = function () {
                        if (isDateRange) {
                            return (!(controller.$error.daterange && (controller.$dirty || scope.form.$dirty) && isEndDate));
                        }
                        return true;
                    };

                    scope.validRequired = function () {
                        if (isRequired) {
                            return (!(controller.$error.required && controller.$dirty));
                        }
                        return true;
                    };

                    scope.validFutureDate = function () {
                        if (onlyFutureDates) {
                            return (!(controller.$error.futuredate && controller.$dirty));
                        }
                        return true;
                    };

                    scope.validPastDate = function () {
                        if (onlyPastDates) {
                            return (!(controller.$error.pastdate && controller.$dirty));
                        }
                        return true;
                    };

                    var setRequiredValidity = function (valid) {
                        controller.$setValidity('required', valid);
                    };

                    var setFutureDateValidity = function (valid) {
                        controller.$setValidity('futuredate', valid);
                    };

                    var setPastDateValidity = function (valid) {
                        controller.$setValidity('pastdate', valid);
                    };

                    var setDateRangeValidity = function (valid) {
                        controller.$setValidity('daterange', valid);
                    };

                    var hasInputValue = function (input) {
                        return input.length > 0;
                    };
                }            
        };
    }]);
})();
So, if we comment the offending line in the Render method which I indicated with the comment, everything is correct and my API controller is returning correct model. If we keep that line, when after I click on the Create Daily I'm getting a prompt first 'Do you want to cancel changes' and after I answer 'Yes' the Init of my controller is not firing. I was originally thinking that I'm getting the problem described in the issue, but yesterday I decided to try to eliminate that dirty problem first. My colleague pointed me to this line, we commented it out and thus concluded it was indeed the bug in the directive causing the issue.

So, if we're to keep the original directive, we need to somehow reset control's dirty state back to pristine after applying that line. Or we want to switch to something different. That my colleague started to work on incorporating another control into directive, but he is leaving the project now.
If it's not broken, fix it until it is.


My Blog
Previous
Reply
Map
View

Click here to load this message in the networking platform