Search code examples
angularjsangularjs-directiveangularjs-scope

How to implement 'Open on focus' and 'monthpicker' to Angularjs calendar?


I am new to Angularjs, how can I add open on focus implementation (similar as Microsoft Windows 7 calendar) to my calendar as given below where we using Angular material version1.0.1. We decided not to update our Angular material to 1.1.1.

<!doctype html>
<html ng-app="datepickerBasicUsage">

<head>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.0.1/angular-material.css">
</head>

<body>

  <div ng-controller="AppCtrl" style='padding: 40px;'>
    <md-content>
      <md-datepicker ng-model="birthdate" placeholder="Enter date" md-max-date="maxDate" ng-focus="open()"></md-datepicker>
    </md-content>
  </div>

  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular-animate.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular-aria.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.0.1/angular-material.js"></script>
  <script src="app.js"></script>

</body>

</html>

app.js---

angular.module('datepickerBasicUsage', ['ngMaterial'])
  .controller('AppCtrl', function($scope, $timeout) {
    $scope.birthdate = new Date();

    $scope.maxDate = new Date(
      $scope.birthdate.getFullYear(),
      $scope.birthdate.getMonth(),
      $scope.birthdate.getDate());

    $scope.open = function() {
      $timeout(function() {
        $scope.birthdate = new Date();
        $scope.maxDate = new Date(
          $scope.birthdate.getFullYear(),
          $scope.birthdate.getMonth(),
          $scope.birthdate.getDate());
      }, 400);
    }
  });          

Solution

  • Create a directive named the same as the existing datepicker directive. This way you won't have to add anything to the HTML, as you are already using md-datepicker.

    app.directive('mdDatepicker', function() {
    
      return {
        link: function(scope, element) {
    
          var controller = element.controller('mdDatepicker');
    
          var event = {
            target: document.body
          };
    
          var input = element.find('input');
    
          input.on('focus', function(e) {
    
            scope.$apply(function() {
              controller.openCalendarPane(event);
            });
          });
    
          input.on('click', function(e) {
    
            e.stopPropagation();
          });
        }
      };
    });
    

    The directive retrieves the controller that the normal mdDatepicker directive uses and simply calls the openCalendarPane method when the input is focused.

    The fake event object is needed since mdDatepicker uses event.target internally to know what to set focus to when the datepicker is closed.

    The click listener and event.stopPropagation is needed for it to work in Firefox (at least in the version I tested with), or the calendar would just close instantly again if opened by clicking inside the input.

    Demo: http://plnkr.co/edit/YdtVwa3MnTyrl5wS1eZ8?p=preview