Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular - ui-bootstrap - datepicker - Is there a way of detecting when the month has changed?

I'd like to set the date to the 1st of the currently selected month when it's changed.

is there a way without monitoring click event on the month's buttons?

tia Sam

like image 420
sambomartin Avatar asked Nov 14 '14 11:11

sambomartin


2 Answers

Datepicker's scope has a property 'activeDateId' that you can watch. It changes when you switch months.

I think the best way is to create a 'decorator' on the datepicker directive so you can add functionality to it. You can access its scope if you override the directive's 'compile' function.

You do this in a module.config function:

$provide.decorator('datepickerDirective', function($delegate) {
    var directive = $delegate[0];

    /* Override compile */
    var link = directive.link;

    directive.compile = function() {
        return function(scope, element, attrs, ctrl) {
            link.apply(this, arguments);

            scope.$watch('activeDateId', function() {
              console.log('switched');
            });
        }
    };

    return $delegate;
});

EDIT

I ran into an issue with code similar to this where if you switch the month and the 2 months have their 1st date on the same day, the $watch won't fire. You can get around this by watching the value of the property 'activeDate' in Datepicker's controller:

            scope.$watch(function() {
              return ctrl.activeDate.getTime();
            }, function() {
              console.log('switched');
            });
like image 181
spongessuck Avatar answered Oct 24 '22 11:10

spongessuck


It's unfortunate that the datepicker directive doesn't have a binding to track the month/year change. Here is what I came up with as a work around.

In the html...

<datepicker month-changed="changeMonth($month, $year)" />

In the controller...

$scope.changeMonth = function(month, year) {
    console.log(month, year);
    // set your date here if you need to
    // in my case, i needed to request some results via ajax
};

The decorator

app.config(function($provide) {
    $provide.decorator('datepickerDirective', function($delegate) {
        var directive = $delegate[0],
            link = directive.link;

        angular.extend(directive.scope, { 'monthChanged': '&' });

        directive.compile = function() {
            return function(scope, element, attrs, ctrl) {
                link.apply(this, arguments);

                scope.$watch(function() {
                    return ctrl[0].activeDate.getTime();
                }, function(newVal, oldVal) {
                    if (scope.datepickerMode == 'day') {
                        oldVal = moment(oldVal).format('MM-YYYY');
                        newVal = moment(newVal).format('MM-YYYY');

                        if (oldVal !== newVal) {
                            var arr = newVal.split('-');
                            scope.monthChanged({ '$month': arr[0], '$year': arr[1] });
                        }
                    }
                });
            };
        };
        return $delegate;
    });
});

Another decorator

Here is another decorator that also works but probably a bit more resource intensive. All I do here is override the isActive() method already used in the directive so my method is called instead. I'm only posting this as an example of the possible ways to work around some of the restrictions in 3rd party directives. I don't necessarily recommend this solution.

app.config(function($provide) {
    $provide.decorator('datepickerDirective', function($delegate) {
        var directive = $delegate[0],
            link = directive.link;

        angular.extend(directive.scope, { 'monthChanged': '&' });

        directive.compile = function() {
            return function(scope, element, attrs, ctrl) {
                link.apply(this, arguments);

                var activeMonth, activeYear, activeFn = scope.isActive;

                scope.isActive = function(dateObj) {
                    if (scope.datepickerMode == 'day' && !dateObj.secondary) {
                        var oldMonth = activeMonth, oldYear = activeYear,
                            newMonth = dateObj.date.getMonth() + 1;
                            newYear = dateObj.date.getFullYear();

                        if (oldMonth !== newMonth || oldYear !== newYear) {
                            activeMonth = newMonth;
                            activeYear = newYear;
                            scope.monthChanged({ '$month': newMonth, '$year': newYear });
                        }
                    }
                    activeFn(dateObj);
                };
            };
        };
        return $delegate;
    });
});
like image 30
Rob Avatar answered Oct 24 '22 10:10

Rob