Skip to content

Instantly share code, notes, and snippets.

@cgmartin
Last active May 21, 2018 21:42
Show Gist options
  • Save cgmartin/3daa01f910601ced9cd3 to your computer and use it in GitHub Desktop.
Save cgmartin/3daa01f910601ced9cd3 to your computer and use it in GitHub Desktop.
Send an event to refresh view of ui-bootstrap datepicker
/**
* Decorates the ui-bootstrap datepicker directive's controller to allow
* refreshing the datepicker view (and rerunning invalid dates function)
* upon an event trigger: `$scope.$broadcast('refreshDatepickers');`
*
* Works with inline and popup. Include this after `ui.bootstrap` js
*/
angular.module('ui.bootstrap.datepicker')
.config(function($provide) {
$provide.decorator('datepickerDirective', function($delegate) {
var directive = $delegate[0];
var link = directive.link;
directive.compile = function() {
return function(scope, element, attrs, ctrls) {
link.apply(this, arguments);
var datepickerCtrl = ctrls[0];
var ngModelCtrl = ctrls[1];
if (ngModelCtrl) {
// Listen for 'refreshDatepickers' event...
scope.$on('refreshDatepickers', function refreshView() {
datepickerCtrl.refreshView();
});
}
}
};
return $delegate;
});
});
@spirau
Copy link

spirau commented Oct 20, 2015

Using version 0.14.2, you need to change "datepickerDirective" to "uibDatepickerDirective".

I wanted to update the datepicker on locale change using dynamic-locale. So I directly connected the datepicker refresh to the $localeChnageSuccess event. That's working but only for the month name. The labels for the days are well defined in the new language in the datePicker array due to the forced refreshView, but not refreshed by angular itself.
Even when playing manually with the datepicker the day names dont't change. The new day names only appear if I go to a month view then back to the day view. Where is the binding!

Any idea how to fix it?

 .config(function ($provide) {
        $provide.decorator('uibDatepickerDirective', function($delegate) {
            angular.forEach($delegate, function (directive) {
                var originalCompile = directive.compile;
                var originalLink = directive.link;
                if (originalCompile) {
                    directive.compile = function () {
                        return function (scope, element, attrs, ctrls) {
                            originalLink.apply(this, arguments);
                            var datepickerCtrl = ctrls[0];
                            var ngModelCtrl = ctrls[1];
                            if (ngModelCtrl) {
                                scope.$on('$localeChangeSuccess', function () {
                                    datepickerCtrl.refreshView();
                                });
                            }
                        };
                    }
                }
            });
            return $delegate;
        });
    })
...

@jitendra19
Copy link

I want to stop scrolling calendra to go next month and next year. In short, I want to disable arrows(move) and toggling in calendar header. I tried to decorate using below code


angular.module('ui.bootstrap.datepicker') .config(function($provide) { $provide.decorator('uibDatepickerDirective', function($delegate)


but getting error like Uncaught Error: [$injector:modulerr] Failed to instantiate module myApp due to: Error: [$injector:modulerr] Failed to instantiate module ui.bootstrap due to: Error: [$injector:modulerr] Failed to instantiate module ui.bootstrap.datepicker due to: Error: [$injector:unpr] Unknown provider: uibDatepickerDirectiveProvider


it looks like injection error.

@thescientist13
Copy link

This was very helpful and worked perfectly for me! Thank you very much.

@MadsMadsDk
Copy link

@extropianer
Did you ever find a none-flicker-solution to this issue? :)

@oomkoos
Copy link

oomkoos commented Jan 22, 2016

@spirau or anyone else using uib-datepicker and dynamic-locale ....
did you manage to get the uib-datepicker day names to also refresh?

@rknell
Copy link

rknell commented Apr 14, 2016

@MadsMadsDk

This works for me and doesn't flicker:

super hacky but this seems to work for me. Replace the function in datepicker. I tried using $timeout but it didn't like it.

Step 1. Change the signature to include a retry variable. Its just a placeholder to kill the infinite loop.

this.refreshView = function(retry) {

Step 2. Add this to the end of the function

if(!retry){
      setTimeout(function () {
        console.log("Refreshing view timeout");
        _this.refreshView(true);
        $scope.$apply();
      }, 1);
    }

Full Code:

this.refreshView = function(retry) {
    console.log("Refresh view");
    if (this.element) {
      $scope.selectedDt = null;
      this._refreshView();
      if ($scope.activeDt) {
        $scope.activeDateId = $scope.activeDt.uid;
      }

      var date = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
      date = dateParser.fromTimezone(date, ngModelOptions.timezone);
      ngModelCtrl.$setValidity('dateDisabled', !date ||
        this.element && !this.isDisabled(date));
    }
    var _this = this;

    if(!retry){
      setTimeout(function () {
        console.log("Refreshing view timeout");
        _this.refreshView(true);
        $scope.$apply();
      }, 1);
    }

  };

@haskelcurry
Copy link

@oomkoos, @spirau

I suppose that the cause of the problem here is the one-time binding (ng-repeat="label in ::labels track by $index").
So that the day names get refreshed only when the ng-switch-when condition gets changed, as @spirau mentioned. So, the ugly workaround would be to force this change automatically:

scope.$on('$localeChangeSuccess', function () {
    const mode = scope.datepickerMode;
    scope.datepickerMode = '';
    scope.$$postDigest(() => {
        scope.datepickerMode = mode;
    });
    datepickerCtrl.refreshView();
});

It will flicker when opened, though, but that's not critical for me. The datepicker is always closed when the locale gets changed.

If anyone else figured out a better solution please let us know!
Cheers.

@nlitwin
Copy link

nlitwin commented May 21, 2018

If you're using a later version of Angular UI Bootstrap, replace:
$provide.decorator('datepickerDirective', function($delegate) {
with
$provide.decorator('uibDatepickerDirective', function($delegate) {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment