Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Bert Detailleur 21 posts 122 karma points
    Sep 17, 2018 @ 06:29
    Bert Detailleur
    0

    Datepickers: Why is the standard umbraco datepicker different (and better) than the api docs version?

    Hi all!

    When fooling around with reusable umbraco directives I noticed the differences between the standard datepicker and the reusable datepicker from the api docs.

    The standard one is much more complete, e.g. having localization options, a clear button, cross browser compatible,... ready and available.

    The datepicker from the api documentation is lacking all that, which could be annoying to users.

    Why not make the standard datepicker the one we should use in the api docs? Make that one the directive. Makes much more sense!

  • Dave Woestenborghs 3504 posts 12133 karma points MVP 8x admin c-trib
    Sep 19, 2018 @ 13:11
    Dave Woestenborghs
    0

    Hi Bert,

    Can you point out what differences there are ? Maybe some screenshots are usefull.

    Also can you provide the link of the documentation you are talking about.

    Dave

  • Bert Detailleur 21 posts 122 karma points
    Sep 19, 2018 @ 14:20
    Bert Detailleur
    0

    Sure!

    In short there are 2 directives. umbDateTimePicker and umbDatePicker UmbDatePicker is documented on de api docs here:

    https://our.umbraco.com/apidocs/ui/#/api/umbraco.directives.directive:umbDateTimePicker

    However, the other one, which the standard umbraco datepicker uses, is not documented.

    the documented directive when put into html looks like this:

    <div class="datepicker" style="position: relative;">
    
    <div ng-hide="hasTranscludedContent" class="input-append date">
        <input data-format="{{ options.format }}" type="text" class="datepickerinput" />
        <span class="add-on">
            <i class="icon-calendar"></i>
        </span>
    </div>
    
    <div class="js-datePicker__transcluded-content" ng-transclude></div>
    

    but the standard one looks like this:

    <div class="umb-editor umb-datepicker" ng-controller="Umbraco.PropertyEditors.DatepickerController">
    <ng-form name="datePickerForm">
        <div class="input-append date datepicker" style="position: relative;" id="datepicker{{model.alias}}">
    
            <input name="datepicker" data-format="{{model.config.format}}" id="{{model.alias}}" type="text"
                   ng-model="datetimePickerValue"
                   ng-required="model.validation.mandatory"
                   val-server="value"
                   class="datepickerinput" />
    
            <span class="add-on">
                <i class="icon-calendar"></i>
            </span>
    
        </div>
    
        <span class="help-inline" val-msg-for="datepicker" val-toggle-msg="required"><localize key="general_required">Required</localize></span>
        <span class="help-inline" val-msg-for="datepicker" val-toggle-msg="valServer">{{datePickerForm.datepicker.errorMsg}}</span>
        <span class="help-inline" val-msg-for="datepicker" val-toggle-msg="pickerError"><localize key="validation_invalidDate">Invalid date</localize></span>
    
        <p ng-if="model.config.offsetTime === '1' && serverTimeNeedsOffsetting && model.value" class="muted">
            <small><localize key="content_scheduledPublishServerTime">This translates to the following time on the server:</localize> {{serverTime}}</small><br/>
            <small><localize key="content_scheduledPublishDocumentation">What does this mean?</localize></small>
        </p>
        <p ng-show="hasDatetimePickerValue === true || datePickerForm.datepicker.$error.pickerError === true">
            <a href ng-click="clearDate()"><i class="icon-delete"></i><small><localize key="content_removeDate">Clear date</localize></small></a>
        </p>
    
    </ng-form>
    

    The controllers are very different as well

    controller example according to api:

    (function () {
    "use strict";
    
    function Controller() {
    
           var vm = this;
    
           vm.date = "";
    
           vm.config = {
               pickDate: true,
               pickTime: true,
               useSeconds: true,
               format: "YYYY-MM-DD HH:mm:ss",
               icons: {
                   time: "icon-time",
                   date: "icon-calendar",
                   up: "icon-chevron-up",
                   down: "icon-chevron-down"
               }
           };
    
           vm.datePickerChange = datePickerChange;
           vm.datePickerError = datePickerError;
    
           function datePickerChange(event) {
               // handle change
               if(event.date && event.date.isValid()) {
                   var date = event.date.format(vm.datePickerConfig.format);
               }
           }
    
           function datePickerError(event) {
               // handle error
           }
    
       }
    
    angular.module("umbraco").controller("My.Controller", Controller);
    

    controller the standard version uses:

    function datepickerController($scope, notificationsService, assetsService, angularHelper, userService, $element, dateHelper) {
    
    //setup the default config
    var config = {
        pickDate: true,
        pickTime: false,
        useSeconds: false,
        format: "DD-MM-YYYY",
        icons: {
            time: "icon-time",
            date: "icon-calendar",
            up: "icon-chevron-up",
            down: "icon-chevron-down"
        }
    
    };
    
    
    //map the user config
    $scope.model.config = angular.extend(config, $scope.model.config);
    //ensure the format doesn't get overwritten with an empty string
    if ($scope.model.config.format === "" || $scope.model.config.format === undefined || $scope.model.config.format === null) {
        $scope.model.config.format = $scope.model.config.pickTime ? "DD-MM-YYYY HH:mm:ss" : "DD-MM-YYYY";
    }
    
    $scope.hasDatetimePickerValue = $scope.model.value ? true : false;
    $scope.datetimePickerValue = null;
    
    //hide picker if clicking on the document 
    $scope.hidePicker = function () {
        //$element.find("div:first").datetimepicker("hide");
        // Sometimes the statement above fails and generates errors in the browser console. The following statements fix that.
        var dtp = $element.find("div:first");
        if (dtp && dtp.datetimepicker) {
            dtp.datetimepicker("hide");
        }
    };
    $(document).bind("click", $scope.hidePicker);
    
    
    function getCurrentDateAsString() {
        var today = new Date();
        var dd = today.getDate();
        var mm = today.getMonth() + 1; //January is 0!
    
        var yyyy = today.getFullYear();
        if (dd < 10) {
            dd = '0' + dd;
        }
        if (mm < 10) {
            mm = '0' + mm;
        }
        var today = yyyy + '-' + mm + '-' + dd;
        return today;
    }
    
    //handles the date changing via the api
    function applyDate(e) {
        angularHelper.safeApply($scope, function () {
            // when a date is changed, update the model
            if (e.date && e.date.isValid()) {
                $scope.datePickerForm.datepicker.$setValidity("pickerError", true);
                $scope.hasDatetimePickerValue = true;
                $scope.datetimePickerValue = e.date.format($scope.model.config.format);
            }
            else {
                $scope.hasDatetimePickerValue = false;
                $scope.datetimePickerValue = null;
            }
    
            setModelValue();
    
            if (!$scope.model.config.pickTime) {
                $element.find("div:first").datetimepicker("hide", 0);
            }
        });
    }
    
    //sets the scope model value accordingly - this is the value to be sent up to the server and depends on 
    // if the picker is configured to offset time. We always format the date/time in a specific format for sending
    // to the server, this is different from the format used to display the date/time.
    function setModelValue() {
        if ($scope.hasDatetimePickerValue) {
            var elementData = $element.find("div:first").data().DateTimePicker;
            if ($scope.model.config.pickTime) {
                //check if we are supposed to offset the time
                if ($scope.model.value && $scope.model.config.offsetTime === "1" && Umbraco.Sys.ServerVariables.application.serverTimeOffset !== undefined) {
                    $scope.model.value = dateHelper.convertToServerStringTime(elementData.getDate(), Umbraco.Sys.ServerVariables.application.serverTimeOffset);
                    $scope.serverTime = dateHelper.convertToServerStringTime(elementData.getDate(), Umbraco.Sys.ServerVariables.application.serverTimeOffset, "YYYY-MM-DD HH:mm:ss Z");
                }
                else {
                    $scope.model.value = elementData.getDate().format("YYYY-MM-DD HH:mm:ss");
                }
            }
            else {
                $scope.model.value = elementData.getDate().format("YYYY-MM-DD");
            }
        }
        else {
            $scope.model.value = null;
        }
    }
    
    var picker = null;
    
    $scope.clearDate = function () {
        $scope.hasDatetimePickerValue = false;
        $scope.datetimePickerValue = null;
        $scope.model.value = null;
        $scope.datePickerForm.datepicker.$setValidity("pickerError", true);
    }
    
    $scope.serverTime = null;
    $scope.serverTimeNeedsOffsetting = false;
    if (Umbraco.Sys.ServerVariables.application.serverTimeOffset !== undefined) {
        // Will return something like 120
        var serverOffset = Umbraco.Sys.ServerVariables.application.serverTimeOffset;
    
        // Will return something like -120
        var localOffset = new Date().getTimezoneOffset();
    
        // If these aren't equal then offsetting is needed
        // note the minus in front of serverOffset needed 
        // because C# and javascript return the inverse offset
        $scope.serverTimeNeedsOffsetting = (-serverOffset !== localOffset);
    }
    
    //get the current user to see if we can localize this picker
    userService.getCurrentUser().then(function (user) {
    
        assetsService.loadCss('lib/datetimepicker/bootstrap-datetimepicker.min.css', $scope).then(function () {
    
            var filesToLoad = ["lib/datetimepicker/bootstrap-datetimepicker.js"];
    
    
            $scope.model.config.language = user.locale;
    
    
            assetsService.load(filesToLoad, $scope).then(
                function () {
                    //The Datepicker js and css files are available and all components are ready to use.
    
                    // Get the id of the datepicker button that was clicked
                    var pickerId = $scope.model.alias;
    
                    var element = $element.find("div:first");
    
                    // Open the datepicker and add a changeDate eventlistener
                    element
                        .datetimepicker(angular.extend({ useCurrent: true }, $scope.model.config))
                        .on("dp.change", applyDate)
                        .on("dp.error", function (a, b, c) {
                            $scope.hasDatetimePickerValue = false;
                            $scope.datePickerForm.datepicker.$setValidity("pickerError", false);
                        });
    
                    if ($scope.hasDatetimePickerValue) {
                        var dateVal;
                        //check if we are supposed to offset the time
                        if ($scope.model.value && $scope.model.config.offsetTime === "1" && $scope.serverTimeNeedsOffsetting) {
                            //get the local time offset from the server
                            dateVal = dateHelper.convertToLocalMomentTime($scope.model.value, Umbraco.Sys.ServerVariables.application.serverTimeOffset);
                            $scope.serverTime = dateHelper.convertToServerStringTime(dateVal, Umbraco.Sys.ServerVariables.application.serverTimeOffset, "YYYY-MM-DD HH:mm:ss Z");
                        }
                        else {
                            //create a normal moment , no offset required
                            var dateVal = $scope.model.value ? moment($scope.model.value, "YYYY-MM-DD HH:mm:ss") : moment();
                        }
    
                        element.datetimepicker("setValue", dateVal);
                        $scope.datetimePickerValue = dateVal.format($scope.model.config.format);
                    }
    
                    element.find("input").bind("blur", function () {
                        //we need to force an apply here
                        $scope.$apply();
                    });
    
                    //Ensure to remove the event handler when this instance is destroyted
                    $scope.$on('$destroy', function () {
                        element.find("input").unbind("blur");
                        element.datetimepicker("destroy");
                    });
    
    
                    var unsubscribe = $scope.$on("formSubmitting", function (ev, args) {
                        setModelValue();
                    });
                    //unbind doc click event!
                    $scope.$on('$destroy', function () {
                        unsubscribe();
                    });
    
    
                });
        });
    
    });
    
    var unsubscribe = $scope.$on("formSubmitting", function (ev, args) {
        setModelValue();
    });
    
    //unbind doc click event!
    $scope.$on('$destroy', function () {
        $(document).unbind("click", $scope.hidePicker);
        unsubscribe();
    });
    

    } angular.module("umbraco").controller("datepickerController", datepickerController);

Please Sign in or register to post replies

Write your reply to:

Draft