Jon Jon - 6 months ago 160
Javascript Question

AngularJS uib-datepicker doesn't display calendar after the first open/close event while in uib-tab

Versions:


  • Bootstrap 3.5.5

  • AngularJS 1.4.7

  • AngularUIB 0.14.3



I'm using a uib-datepicker in a tabbed page (using uib-tabset)

<uib-tabset type='pills'>
<uib-tab heading='HeatMap'>
<input id='dateToImport' type='text' uib-datepicker-popup='yyyy-MM-dd' class='form-control' ng-change='changeDateTime()' ng-model='selectedDate' is-open='isDatePickerOpen' ng-click='openDatePicker($event)'/>
</uib-tab>
</uib-tabset>


Controller Code:

app.controller("ctrl", function($scope, heatMapSvc){
$scope.isDatePickerOpen = false;

$scope.openDatePicker = function(){
$scope.isDatePickerOpen = true;
console.log("isDatePickerOpen?", $scope.isDatePickerOpen);
};
});


When selecting the input, the datepicker calendar appears as expected. Once I select a date, however, the datepicker calendar won't reappear on selection. I've unfocused and refocused the control, nothing. In my
openDatePicker
method call, I write to console to ensure that the method is getting called. Every time I select the datepicker (regardless if the calendar appears), the controller is firing the correct method.

I've taken the datepicker out of the tabset that it was wrapped in, and the datepicker works correctly (shows the calendar every selection). I need this datepicker to work correctly within the uib-tabset that I've defined.

Answer

Problem is in "scope inheritance". Angular scopes is based on js objects prototype inheritance.

So... $scope <- { uib-tables scope } <- { uib-tab scope } and at first time, { uib-tab scope } has not isDatePickerOpen property an it taken from $scope, but after poput is closed, { uib-tab scope } will take own isDatePickerOpen than equal false, and the latter is more a priority. Function openDatePicker changing $scope.isDatePickerOpen, but calendar directive will take value from { uib-tab scope }.

you can see it if will add {{isDatePickerOpen}} after input tag. After first time calendar opened it always wil be false.

Solution

1) use controllerAs syntax. recommended

controller:

  var vm = this;

  vm.isDatePickerOpen = false;

  vm.openDatePicker = function(){
    vm.isDatePickerOpen = true;
    console.log("isDatePickerOpen?", vm.isDatePickerOpen);
  };

template:

<uib-tabset type='pills'>
  <uib-tab heading='HeatMap'>
    <input id='dateToImport' type='text' uib-datepicker-popup='yyyy-MM-dd' class='form-control' ng-change='changeDateTime()' ng-model='selectedDate' is-open='vm.isDatePickerOpen' ng-click='vm.openDatePicker($event)'/>
  </uib-tab>
</uib-tabset>

Note! For using this solution you must using controller as syntax for declare a controller.

<body ng-controller="SettingsController1 as vm">
...
</body>

or if you use ui-router

$stateProvider.state('contacts', {
  templateUrl: '...',
  controller: function(){
    ...
  },
  controllerAs: 'vm'
})

2) access to parent scope. not recommended for information

controlller:

  $scope.isDatePickerOpen = false;

  $scope.openDatePicker = function(){
    $scope.isDatePickerOpen = true;
    console.log("isDatePickerOpen?", $scope.isDatePickerOpen);
  };

template:

<uib-tabset type='pills'>
  <uib-tab heading='HeatMap'>
    <input id='dateToImport' type='text' uib-datepicker-popup='yyyy-MM-dd' class='form-control' ng-change='changeDateTime()' ng-model='selectedDate' is-open='$parent.$parent.isDatePickerOpen' ng-click='openDatePicker($event)'/>
  </uib-tab>
</uib-tabset>

3) use object. can be use, but first sulution is better

controller:

  $scope.status = {isDatePickerOpen : false};

  $scope.openDatePicker = function(){
    $scope.status.isDatePickerOpen = true;
    console.log("isDatePickerOpen?", $scope.status.isDatePickerOpen);
  };

template:

<uib-tabset type='pills'>
  <uib-tab heading='HeatMap'>
    <input id='dateToImport' type='text' uib-datepicker-popup='yyyy-MM-dd' class='form-control' ng-change='changeDateTime()' ng-model='selectedDate' is-open='status.isDatePickerOpen' ng-click='openDatePicker($event)'/>
  </uib-tab>
</uib-tabset>

May be this article explain topic more detail. http://www.undefinednull.com/2014/02/11/mastering-the-scope-of-a-directive-in-angularjs/

Comments