Phil.Wheeler
Phil.Wheeler

Reputation: 16848

Set Umbraco Property Editor Input to jQueryUI Datepicker

I'm close but still can't quite get this to work.

I have a new custom property editor that is loading correctly and is doing almost everything expected until I try to set the text field to be a jQuery UI element.

As soon as I add a directive in Angular for setting it to call the jQuery UI datepicker function, I get the following error suggesting it hasn't loaded the jQueryUI script library correctly:

TypeError: Object [object Object] has no method 'datepicker'

Trouble is, I can't see where I should be adding it as the logical places (to my mind, at least) seem to make no difference. Here is the code in full:

function MultipleDatePickerController($scope, assetsService) {

    //tell the assetsService to load the markdown.editor libs from the markdown editors
    //plugin folder
    //assetsService
    //    .load([
    //        "http://code.jquery.com/ui/1.10.4/jquery-ui.min.js"
    //    ])
    //    .then(function () {
    //        //this function will execute when all dependencies have loaded            
    //    });

    //load the seperat css for the editor to avoid it blocking our js loading
    assetsService.loadCss("/css/jquery-ui.custom.min.css");

    if (!$scope.model.value) {
        $scope.model.value = [];
    }

    //add any fields that there isn't values for
    //if ($scope.model.config.min > 0) {
    if ($scope.model.value.length > 0) {
        for (var i = 0; i < $scope.model.value.length; i++) {
            if ((i + 1) > $scope.model.value.length) {
                $scope.model.value.push({ value: "" });
            }
        }
    }

    $scope.add = function () {
        //if ($scope.model.config.max <= 0 || $scope.model.value.length < $scope.model.config.max) {
        if ($scope.model.value.length <= 52) {
            $scope.model.value.push({ value: "" });
        }
    };

    $scope.remove = function (index) {
        var remainder = [];
        for (var x = 0; x < $scope.model.value.length; x++) {
            if (x !== index) {
                remainder.push($scope.model.value[x]);
            }
        }
        $scope.model.value = remainder;
    };

}

var datePicker = angular.module("umbraco").controller("AcuIT.MultidateController", MultipleDatePickerController);

datePicker.directive('jqdatepicker', function () {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, element, attrs, ngModelCtrl) {
            $(function () {
                element.datepicker({
                    dateFormat: 'dd/mm/yy',
                    onSelect: function (date) {
                        scope.$apply(function () {
                            ngModelCtrl.$setViewValue(date);
                        });
                    }
                });
            });
        }
    }
});

Upvotes: 2

Views: 1765

Answers (2)

Phil.Wheeler
Phil.Wheeler

Reputation: 16848

Got it. This is probably a bit of a hack, but it's simple and effective so it's a win nonetheless.

The assetsService call is the key, where I've put code into the deferred .then statement to call jQueryUI's datepicker on any item that has the "jqdp" CSS class:

//tell the assetsService to load the markdown.editor libs from the markdown editors
//plugin folder
assetsService
    .load([
        "/App_Plugins/Multidate/jquery-ui.min.js"
    ])
    .then(function () {
        //this function will execute when all dependencies have loaded    
        $('.jqdp').datepicker({ dateFormat: 'dd/mm/yy' });
    });

I've then gone and added that class to my view:

    <input type="text" jqdatepicker name="item_{{$index}}" ng-model="item.value" class="jqdp" id="dp-{{model.alias}}-{{$index}}" />

Finally, I've added a directive to ensure that dynamically-added items also display a datepicker:

datePicker.directive('jqdatepicker', function () {

    return function (scope, element, attrs) {

        scope.$watch("jqdatepicker", function () {
            try{
                $(element).datepicker({ dateFormat: 'dd/mm/yy' });
            }
            catch(e)
            {}
        });
    };

});

As I said, this is possibly a bit hacky but it achieves the right result and seems like a simple solution.

Upvotes: 1

Dan Diplo
Dan Diplo

Reputation: 25339

I faced the same problem when adapting a jQuery Date Range Picker for my Date Range Picker package for Umbraco 7. It's frustrating! The problem (I think) is that Angular's ng-model listens for "input" changes to trigger events and so doesn't pick up on a jQuery triggered event.

The way around it I found was to force the input event of the element you wish to update to fire manually, using jQuery's .trigger() event.

For example, the date picker I was using had this code for when a date was changed:

updateInputText: function () {
    if (this.element.is('input')) {
        this.element.val(this.startDate.format(this.format) + this.separator + this.endDate.format(this.format));
    }
},

I just adapted it to force an input trigger by adding this.element.trigger('input') to the code block, so it now reads:

updateInputText: function () {
    if (this.element.is('input')) {
        this.element.val(this.startDate.format(this.format) + this.separator + this.endDate.format(this.format));
        this.element.trigger('input');
    }
},

This forces Angular to "see" the change and then ng-model is updated. There may well be a more elegant way (as I'm an Angular newbie), but I know this worked for me.

Upvotes: 2

Related Questions