4

I'm using the Angular UI Bootstrap datepicker in day mode for my project. How can I get the currently open month befoure the new date was selected?

3 Answers 3

2

After spending some time trying to find the right way to get this, and upon reading the datepicker directive and controller's source code, I couldn't find a non-dirty way of getting the current month, so here's a very hacky way to do it.

Warning, future versions of Angular-UI Bootstrap might break this approach

You will need to use AngularJS decorators for this. Here's the code you'll need:

angular.module('myApp', [
  'ui.bootstrap'
  ])
  .config(function($provide) {
    $provide.decorator('datepickerDirective', function($delegate) {
      var directive = $delegate[0];
      //get a copy of the directive's original compile function
      var directiveCompile = directive.compile;

      //overwrite the original compile function
      directive.compile = function(tElement, tAttrs) {
        // call the directive's compile with apply to send the original 'this' and arguments to it
        var link = directiveCompile.apply(this, arguments);

        //here's where the magic starts
        return function(scope, element, attrs, ctrls) {
          //call the original link
          link.apply(this, arguments);
          //move is the internal function called when you click
          //on the chevrons for previous and next month
          var originalMove = scope.move;
          scope.move = function(direction) {
            originalMove.apply(this, arguments);
            //when move is called, the 'this' object contains a 'title' property with the current month!
            var currentMonth = this.title;
            //emit the event to parent scopes
            scope.$emit('datepicker.monthChanged', currentMonth);
          }
        }
      };

      return $delegate;
    });
  })

And then your controller can listen to datepicker.monthChanged:

$scope.$on('datepicker.monthChanged', function(event, newVal) {
  $scope.currentMonth = newVal;
})

Since the datepicker controller doesn't keep the current selected date within the $scope, it keeps it in a closure variable, the only way I found you could get the selected month was in the this object when the function move is called. move is called whenever you click on the previous and next month icons.

Here's a plunkr demonstrating the usage of this decorator: http://plnkr.co/edit/iWJWjM8nCsh5TMupNioo?p=preview

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks a lot! That is absolutely full answer! )) More than I could expect ))
Since you've already studied the topic, could you please help me with another datepicker functionality extension related question? stackoverflow.com/q/32676310/601726
I'm trying to use this aproach, but I'm getting "Unknown provider: datepickerDirectiveProvider" error, any ideas why?
1

You should use ng-model to assign the selected value to a variable. If you use a raw scope variable, you'd be able to access the Date object at any time. e.g.:

<input ng-model="selectedDate" other-datepicker-directives />

Then in your controller, the selectedDate value is a Date object, to which you can access the normal date properties; like .getMonth(). If you then use a watch, you can have a local variable always set with the currently selected month!

So, in your controller:

$scope.$watch(function() { return $scope.selectedDate }, function()
{
    // bail out if no selected date
    if (!angular.isDefined($scope.selectedDate)) return;

    $scope.selectedMonth = $scope.selectedDate.getMonth();
});

Now whenever $scope.selectedDate gets changed, $scope.selectedMonth will be updated. This means you could display the selected month in your HTML view like so:

<span> Selected Month: {{selectedMonth}}</span>

Hope that helps!

3 Comments

This works when the date has already been selected, the question asks how to get the open month before the date is selected.
@yvesmancera is quite right. I'm trying to get the displayed month (part of the rendered datepicker template), not the selected date month.
Ahh good point - I missed that. Doesn't look like something that could be gotten easily... there's a scope variable "title" in the datepicker view that would have the month in it when in day-view mode... Something like: angular.element(".yourElementSelectorWithDatePickerDirective").controller("DatePicker"). That would give you the controller for the DatePicker and then the Title of the child directive (Day picker) may be accessible.
0
/**
  * 从一维数组中选出出现次数最多的项
  * @param {Array} arr - 一维数组
  * @see https://stackoverflow.com/a/20762472/5819157
  */
function getMaxOccurrence(arr) {
    var arrMap = {},
      maxCount = 0,
      maxEl,
      item;

    var index = 0,
      length = arr.length;

    for (; index < length; index++) {
      if (arr.hasOwnProperty(index)) {
        item = arr[index];
        arrMap.hasOwnProperty(item) ? ++arrMap[item] : (arrMap[item] = 1);
        if (arrMap[item] > maxCount) {
          maxCount = arrMap[item];
          maxEl = item;
        }
      }
    }

    return maxEl;
}
$scope.datetimeOption = {
  customClass: getDayClass,
  showWeeks: false,
}
var tempYear = null;
var prevStartOfMonth = null;
var tempMonthMap = [];

function getDayClass(data) {
  var date = data.date;
  var mode = data.mode;

  var selectedMonth = null;
  var selectedDate = null;
  var startOfMonth = null;
  var endOfMonth = null;

  if (tempMonthMap.length === 42) {
    tempMonthMap = [];
  }

  if (mode === 'day') {
    if (date.getMonth) {
      tempMonthMap.push(moment(date).month());
    }

    if (tempMonthMap.length === 42) {
      tempYear = moment(date).year();

      selectedMonth = getMaxOccurrence(tempMonthMap);

      selectedDate = moment({ year: tempYear, month: selectedMonth });
      startOfMonth = moment(selectedDate).startOf('month').format('YYYY-MM-DD HH:mm:ss');
      endOfMonth = moment(selectedDate).endOf('month').format('YYYY-MM-DD HH:mm:ss');

      if (prevStartOfMonth !== startOfMonth) {
        getListRequest({ since: startOfMonth, until: endOfMonth });
        prevStartOfMonth = startOfMonth;
      }
    }

      //

  }

  return '';
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.