Skip to content

Commit 1f667c1

Browse files
author
Robert Michalski
committed
Rewritten most of the code without altering the the way the directive functions.
Added controls for all settings of jQuery plugin to test app.
1 parent 828d5ec commit 1f667c1

File tree

3 files changed

+357
-192
lines changed

3 files changed

+357
-192
lines changed

slider.js

Lines changed: 143 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -12,158 +12,183 @@ angular.module('ui.bootstrap-slider', [])
1212
value: "=",
1313
ngModel: '=',
1414
range:'=',
15-
sliderid:'=',
16-
formater:'&',
15+
sliderid: '=',
16+
formater: '&',
1717
onStartSlide: '&',
1818
onStopSlide: '&',
1919
onSlide: '&'
2020
},
2121
link: function ($scope, element, attrs, ngModelCtrl, $compile) {
22+
var ngModelDeregisterFn, ngDisabledDeregisterFn;
23+
2224
initSlider();
2325

2426
function initSlider() {
27+
var options = {};
2528

26-
if (attrs.ngChange) {
27-
ngModelCtrl.$viewChangeListeners.push(function () {
28-
$scope.$apply(attrs.ngChange);
29-
});
29+
function setOption(key, value, defaultValue) {
30+
options[key] = value || defaultValue;
3031
}
31-
32-
$.fn.slider.Constructor.prototype.disable = function () {
33-
this.picker.off();
34-
};
35-
36-
$.fn.slider.Constructor.prototype.enable = function () {
37-
this.picker.on();
38-
};
39-
40-
if (attrs.ngChange) {
41-
ngModelCtrl.$viewChangeListeners.push(function () {
42-
$scope.$apply(attrs.ngChange);
43-
});
32+
function setFloatOption(key, value, defaultValue) {
33+
options[key] = value ? parseFloat(value) : defaultValue;
4434
}
45-
46-
var options = {};
47-
if ($scope.sliderid) options.id = $scope.sliderid;
48-
if ($scope.min) options.min = parseFloat($scope.min);
49-
if ($scope.max) {
50-
options.max = parseFloat($scope.max);
35+
function setBooleanOption(key, value, defaultValue) {
36+
options[key] = value ? value + '' === 'true' : defaultValue;
37+
}
38+
function getArrayOrValue(value) {
39+
return (angular.isString(value) && value.indexOf("[") === 0) ? angular.fromJson(value) : value;
5140
}
5241

42+
setOption('id', $scope.sliderid);
43+
setOption('orientation', attrs.orientation, 'horizontal');
44+
setOption('selection', attrs.selection, 'before');
45+
setOption('handle', attrs.handle, 'round');
46+
setOption('tooltip', attrs.tooltip, 'show');
47+
setOption('tooltipseparator', attrs.tooltipseparator, ':');
48+
49+
setFloatOption('min', $scope.min, 0);
50+
setFloatOption('max', $scope.max, 10);
51+
setFloatOption('step', $scope.step, 1);
52+
var strNbr = options.step + '';
53+
var decimals = strNbr.substring(strNbr.lastIndexOf('.') + 1);
54+
setFloatOption('precision', attrs.precision, decimals);
55+
56+
setBooleanOption('tooltip_split', attrs.tooltipsplit, false);
57+
setBooleanOption('enabled', attrs.enabled, true);
58+
setBooleanOption('naturalarrowkeys', attrs.naturalarrowkeys, false);
59+
setBooleanOption('reversed', attrs.reversed, false);
5360

54-
if (attrs.step) options.step = parseFloat($scope.step);
55-
if (attrs.precision) options.precision = parseFloat(attrs.precision);
56-
if (attrs.orientation) options.orientation = attrs.orientation;
5761
if ($scope.value) {
5862
if (angular.isNumber($scope.value) || angular.isArray($scope.value)) {
5963
options.value = $scope.value;
60-
} else if (angular.isString($scope.value)) {
61-
if (attrs.value.indexOf("[") === 0) {
62-
options.value = angular.fromJson($scope.value);
63-
} else {
64+
}
65+
else if (angular.isString($scope.value)) {
66+
options.value = getArrayOrValue($scope.value);
67+
if(!angular.isArray(options.value)) {
6468
options.value = parseFloat($scope.value);
6569
}
6670
}
6771
}
68-
if ($scope.range) {
69-
options.range = $scope.range === true;
70-
}
7172

72-
if (attrs.selection) options.selection = attrs.selection;
73-
if (attrs.tooltip) options.tooltip = attrs.tooltip;
74-
if (attrs.tooltipseparator) options.tooltip_separator = attrs.tooltipseparator;
75-
if (attrs.tooltipsplit) options.tooltip_split = attrs.tooltipsplit === 'true';
76-
if (attrs.handle) options.handle = attrs.handle;
77-
if (attrs.reversed) options.reversed = attrs.reversed === 'true';
78-
if (attrs.enabled) options.enabled = attrs.enabled === 'true';
79-
if (attrs.naturalarrowkeys) options.natural_arrow_keys = attrs.naturalarrowkeys === 'true';
80-
if (attrs.formater) options.formater = $scope.$eval($scope.formater);
81-
82-
if (options.range && !options.value) {
83-
options.value = [0, 0]; // This is needed, because of value defined at $.fn.slider.defaults - default value 5 prevents creating range slider
84-
}
85-
86-
var slider = $(element.find(".slider-input")[0]).slider(options);
87-
slider.slider('destroy');
88-
slider = $(element.find(".slider-input")[0]).slider(options);
89-
90-
var updateEvent;
91-
if (angular.isString(attrs.updateevent)) {
92-
// check if array of event names
93-
if (attrs.updateevent.indexOf("[") === 0) {
94-
updateEvent = angular.fromJson(attrs.updateevent);
73+
setBooleanOption('range', $scope.range, false);
74+
if( options.range ) {
75+
if( angular.isArray($scope.value) ) {
76+
options.value = $scope.value;
77+
}
78+
else if (angular.isString($scope.value)) {
79+
options.value = getArrayOrValue($scope.value);
80+
if(!angular.isArray(options.value)) {
81+
var value = parseFloat($scope.value);
82+
if( value < $scope.min ) {
83+
value = $scope.min;
84+
options.value = [value, options.max];
85+
}
86+
else if( value > $scope.max ) {
87+
value = $scope.max;
88+
options.value = [options.min, value];
89+
}
90+
}
9591
}
9692
else {
97-
// if only single event name in string
98-
updateEvent = [attrs.updateevent];
93+
options.value = [options.min, options.max]; // This is needed, because of value defined at $.fn.slider.defaults - default value 5 prevents creating range slider
9994
}
95+
$scope.ngModel = options.value; // needed, otherwise turns value into [null, ##]
10096
}
10197
else {
102-
// default to slide event
103-
updateEvent = ['slide'];
98+
setFloatOption('value', $scope.value, 5);
10499
}
105100

106-
angular.forEach(updateEvent, function(sliderEvent) {
107-
slider.on(sliderEvent, function(ev) {
108-
ngModelCtrl.$setViewValue(ev.value);
109-
$timeout(function() {
110-
$scope.$apply();
111-
});
112-
});
113-
});
114-
115-
// Event listeners
116-
var sliderEvents = {
117-
slideStart: 'onStartSlide',
118-
slide: 'onSlide',
119-
slideStop: 'onStopSlide'
120-
};
121-
122-
angular.forEach(sliderEvents, function(sliderEventAttr, sliderEvent) {
123-
slider.on(sliderEvent, function(ev) {
124-
125-
if ($scope[sliderEventAttr]) {
126-
var invoker = $parse(attrs[sliderEventAttr]);
127-
invoker($scope.$parent, { $event: ev, value: ev.value });
128-
129-
$timeout(function() {
101+
if ($scope.formater) options.formater = $scope.$eval($scope.formater);
102+
103+
var slider = element.find( ".slider-input" ).eq( 0 );
104+
// check if slider jQuery plugin exists
105+
if( $.fn.slider ) {
106+
// adding methods to jQuery slider plugin prototype
107+
$.fn.slider.Constructor.prototype.disable = function () {
108+
this.picker.off();
109+
};
110+
$.fn.slider.Constructor.prototype.enable = function () {
111+
this.picker.on();
112+
};
113+
114+
// destroy previous slider to reset all options
115+
slider.slider( options );
116+
slider.slider( 'destroy' );
117+
slider.slider( options );
118+
119+
// everything that needs slider element
120+
var updateEvent = getArrayOrValue( attrs.updateevent );
121+
if ( angular.isString( updateEvent ) ) {
122+
// if only single event name in string
123+
updateEvent = [updateEvent];
124+
}
125+
else {
126+
// default to slide event
127+
updateEvent = ['slide'];
128+
}
129+
angular.forEach( updateEvent, function ( sliderEvent ) {
130+
slider.on( sliderEvent, function ( ev ) {
131+
ngModelCtrl.$setViewValue( ev.value );
132+
$timeout( function () {
130133
$scope.$apply();
131-
});
132-
}
133-
});
134-
});
135-
134+
} );
135+
} );
136+
} );
137+
138+
// Event listeners
139+
var sliderEvents = {
140+
slideStart: 'onStartSlide',
141+
slide: 'onSlide',
142+
slideStop: 'onStopSlide'
143+
};
144+
angular.forEach( sliderEvents, function ( sliderEventAttr, sliderEvent ) {
145+
slider.on( sliderEvent, function ( ev ) {
146+
147+
if ( $scope[sliderEventAttr] ) {
148+
var invoker = $parse( attrs[sliderEventAttr] );
149+
invoker( $scope.$parent, {$event: ev, value: ev.value} );
150+
151+
$timeout( function () {
152+
$scope.$apply();
153+
} );
154+
}
155+
} );
156+
} );
157+
158+
if ( attrs.ngChange ) {
159+
ngModelCtrl.$viewChangeListeners.push( function () {
160+
$scope.$apply( attrs.ngChange );
161+
} );
162+
}
136163

137-
if (angular.isDefined(attrs.ngDisabled)) {
138-
$scope.$watch(attrs.ngDisabled, function (value) {
139-
if (value) {
140-
slider.slider('disable');
141-
} else {
142-
slider.slider('enable');
143-
}
144-
});
164+
// deregister ngDisabled watcher to prevent memory leaks
165+
if ( angular.isFunction( ngDisabledDeregisterFn ) ) {
166+
ngDisabledDeregisterFn();
167+
ngDisabledDeregisterFn = null;
168+
}
169+
if ( angular.isDefined( attrs.ngDisabled ) ) {
170+
ngDisabledDeregisterFn = $scope.$watch( attrs.ngDisabled, function ( value ) {
171+
if ( value ) {
172+
slider.slider( 'disable' );
173+
}
174+
else {
175+
slider.slider( 'enable' );
176+
}
177+
} );
178+
}
179+
// deregister ngModel watcher to prevent memory leaks
180+
if ( angular.isFunction( ngModelDeregisterFn ) ) ngModelDeregisterFn();
181+
ngModelDeregisterFn = $scope.$watch( 'ngModel', function ( value ) {
182+
slider.slider( 'setValue', value );
183+
} );
145184
}
185+
}
146186

147-
148-
$scope.$watch('ngModel', function(value) {
149-
slider.slider('setValue', value);
187+
var watchers = ['min', 'max', 'step', 'range'];
188+
angular.forEach(watchers, function(prop) {
189+
$scope.$watch(prop, function(){
190+
initSlider();
150191
});
151-
};
152-
153-
$scope.$watch('max', function(value){
154-
initSlider();
155-
});
156-
157-
$scope.$watch('min', function(value){
158-
initSlider();
159-
});
160-
161-
$scope.$watch('step', function(value){
162-
initSlider();
163-
});
164-
165-
$scope.$watch('range', function(value) {
166-
initSlider();
167192
});
168193
}
169194
};

0 commit comments

Comments
 (0)