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;
});
});
@antoniogiroz
Copy link

Good!
I have same problem. How I can use? I'm noob in AngularJS.
Thanks!

@briangullo
Copy link

Hi algil, not sure if this is still a question for you. To use this is easy.

  1. Create a file called datepicker.decorator.js, and copy and paste the code above into it.
  2. Include it in your project, and add a script reference to datepicker.decorator.js after the script reference for ui-bootstrap.js
  3. In your own code, whenever you need to refresh the calendar, just call $scope.$broadcast('refreshDatepickers');

For example, in my controller I'm using it in a $watch:
$scope.$watch('newLeaveRequest.startDate', function (newVal, oldVal) {
if (newVal) {
$scope.endDateMinDate = newVal;
$scope.$broadcast('refreshDatepickers');
}
});

@xingped
Copy link

xingped commented May 6, 2015

Hey brian, this doesn't seem to work for me. I've even gone so far as to include it inline in ui-bootstrap.js and it throws a dependency error:

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: a

@stephanebruckert
Copy link

Great, it works!

@anoff
Copy link

anoff commented Aug 9, 2015

Wow that is exactly what I was looking for.
I am using the custom-class option in combination with a function that parses $resource data that is available after the initial rendering of the datepicker.
However there seems to be a problem with the pure rendering in my use-case. For the invalidation I can confirm this works. When attaching custom classes however the corresponding function $scope.getDayClass gets called (broadcasting fires correctly) but nothing changes on the datepicker itself. After navigating a month forth and back everything renders as expected.

Anyone got an idea what might help?

EDIT
The following hack does show the classes correctly..but of course causes the datepicker to flicker forth and back

            scope.$on('refreshDatepickers', function refreshView() {
              console.log("refreshing picker");
              datepickerCtrl.activeDate.setMonth(datepickerCtrl.activeDate.getMonth()+1);
              datepickerCtrl.refreshView();
              $timeout(function () {
                datepickerCtrl.activeDate.setMonth(datepickerCtrl.activeDate.getMonth()-1);
                datepickerCtrl.refreshView();
              }, 1);
            });

I couldn't find another combination that works without flickering.

@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