Skip to content
This repository was archived by the owner on May 29, 2019. It is now read-only.

Commit 3f809af

Browse files
committed
fix(timepicker): garbage collect parent watchers
- Garbage collect parent $$watchers on $destroy Closes #5277
1 parent 32b88f1 commit 3f809af

File tree

2 files changed

+42
-18
lines changed

2 files changed

+42
-18
lines changed

src/timepicker/test/timepicker.spec.js

+17
Original file line numberDiff line numberDiff line change
@@ -2191,4 +2191,21 @@ describe('timepicker directive', function() {
21912191
expect(getModelState()).toEqual([14, 40, 25]);
21922192
});
21932193
});
2194+
2195+
describe('gc', function() {
2196+
var $scope;
2197+
beforeEach(inject(function() {
2198+
$scope = $rootScope.$new();
2199+
element = $compile('<uib-timepicker ng-model="time"></uib-timepicker>')($scope);
2200+
$rootScope.$digest();
2201+
}));
2202+
2203+
it('should clean up watchers', function() {
2204+
expect($scope.$$watchers.length > 1).toBe(true);
2205+
2206+
element.isolateScope().$destroy();
2207+
2208+
expect($scope.$$watchers.length).toBe(1);
2209+
});
2210+
});
21942211
});

src/timepicker/timepicker.js

+25-18
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ angular.module('ui.bootstrap.timepicker', [])
1616

1717
.controller('UibTimepickerController', ['$scope', '$element', '$attrs', '$parse', '$log', '$locale', 'uibTimepickerConfig', function($scope, $element, $attrs, $parse, $log, $locale, timepickerConfig) {
1818
var selected = new Date(),
19-
ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl
20-
meridians = angular.isDefined($attrs.meridians) ? $scope.$parent.$eval($attrs.meridians) : timepickerConfig.meridians || $locale.DATETIME_FORMATS.AMPMS;
19+
watchers = [],
20+
ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl
21+
meridians = angular.isDefined($attrs.meridians) ? $scope.$parent.$eval($attrs.meridians) : timepickerConfig.meridians || $locale.DATETIME_FORMATS.AMPMS;
2122

2223
$scope.tabindex = angular.isDefined($attrs.tabindex) ? $attrs.tabindex : 0;
2324
$element.removeAttr('tabindex');
@@ -51,35 +52,35 @@ angular.module('ui.bootstrap.timepicker', [])
5152

5253
var hourStep = timepickerConfig.hourStep;
5354
if ($attrs.hourStep) {
54-
$scope.$parent.$watch($parse($attrs.hourStep), function(value) {
55+
watchers.push($scope.$parent.$watch($parse($attrs.hourStep), function(value) {
5556
hourStep = +value;
56-
});
57+
}));
5758
}
5859

5960
var minuteStep = timepickerConfig.minuteStep;
6061
if ($attrs.minuteStep) {
61-
$scope.$parent.$watch($parse($attrs.minuteStep), function(value) {
62+
watchers.push($scope.$parent.$watch($parse($attrs.minuteStep), function(value) {
6263
minuteStep = +value;
63-
});
64+
}));
6465
}
6566

6667
var min;
67-
$scope.$parent.$watch($parse($attrs.min), function(value) {
68+
watchers.push($scope.$parent.$watch($parse($attrs.min), function(value) {
6869
var dt = new Date(value);
6970
min = isNaN(dt) ? undefined : dt;
70-
});
71+
}));
7172

7273
var max;
73-
$scope.$parent.$watch($parse($attrs.max), function(value) {
74+
watchers.push($scope.$parent.$watch($parse($attrs.max), function(value) {
7475
var dt = new Date(value);
7576
max = isNaN(dt) ? undefined : dt;
76-
});
77+
}));
7778

7879
var disabled = false;
7980
if ($attrs.ngDisabled) {
80-
$scope.$parent.$watch($parse($attrs.ngDisabled), function(value) {
81+
watchers.push($scope.$parent.$watch($parse($attrs.ngDisabled), function(value) {
8182
disabled = value;
82-
});
83+
}));
8384
}
8485

8586
$scope.noIncrementHours = function() {
@@ -128,22 +129,22 @@ angular.module('ui.bootstrap.timepicker', [])
128129

129130
var secondStep = timepickerConfig.secondStep;
130131
if ($attrs.secondStep) {
131-
$scope.$parent.$watch($parse($attrs.secondStep), function(value) {
132+
watchers.push($scope.$parent.$watch($parse($attrs.secondStep), function(value) {
132133
secondStep = +value;
133-
});
134+
}));
134135
}
135136

136137
$scope.showSeconds = timepickerConfig.showSeconds;
137138
if ($attrs.showSeconds) {
138-
$scope.$parent.$watch($parse($attrs.showSeconds), function(value) {
139+
watchers.push($scope.$parent.$watch($parse($attrs.showSeconds), function(value) {
139140
$scope.showSeconds = !!value;
140-
});
141+
}));
141142
}
142143

143144
// 12H / 24H mode
144145
$scope.showMeridian = timepickerConfig.showMeridian;
145146
if ($attrs.showMeridian) {
146-
$scope.$parent.$watch($parse($attrs.showMeridian), function(value) {
147+
watchers.push($scope.$parent.$watch($parse($attrs.showMeridian), function(value) {
147148
$scope.showMeridian = !!value;
148149

149150
if (ngModelCtrl.$error.time) {
@@ -156,7 +157,7 @@ angular.module('ui.bootstrap.timepicker', [])
156157
} else {
157158
updateTemplate();
158159
}
159-
});
160+
}));
160161
}
161162

162163
// Get $scope.hours in 24H mode if valid
@@ -518,6 +519,12 @@ angular.module('ui.bootstrap.timepicker', [])
518519
$scope.blur = function() {
519520
ngModelCtrl.$setTouched();
520521
};
522+
523+
$scope.$on('$destroy', function() {
524+
while (watchers.length) {
525+
watchers.shift()();
526+
}
527+
});
521528
}])
522529

523530
.directive('uibTimepicker', ['uibTimepickerConfig', function(uibTimepickerConfig) {

0 commit comments

Comments
 (0)