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?

Marik
  • 95
  • 8

3 Answers3

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

yvesmancera
  • 2,915
  • 5
  • 24
  • 33
  • Thanks a lot! That is absolutely full answer! )) More than I could expect )) – Marik Sep 20 '15 at 05:30
  • Since you've already studied the topic, could you please help me with another datepicker functionality extension related question? http://stackoverflow.com/q/32676310/601726 – Marik Sep 20 '15 at 06:20
  • I'm trying to use this aproach, but I'm getting "Unknown provider: datepickerDirectiveProvider" error, any ideas why? – tomek550 Oct 24 '16 at 07:57
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!

Scott Byers
  • 3,007
  • 2
  • 17
  • 14
  • This works when the date has already been selected, the question asks how to get the open month before the date is selected. – yvesmancera Sep 18 '15 at 18:14
  • @yvesmancera is quite right. I'm trying to get the displayed month (part of the rendered datepicker template), not the selected date month. – Marik Sep 18 '15 at 18:32
  • 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. – Scott Byers Sep 18 '15 at 18:42
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 '';
}
山茶树和葡萄树
  • 2,050
  • 1
  • 18
  • 18