|
41 | 41 | * Description: Class for "on" state from one of standard bootstrap button types. |
42 | 42 | * Possible values: btn-default, btn-primary, btn-success, btn-info, btn-warning, btn-danger |
43 | 43 | */ |
44 | | - onstyle: 'btn-primary', |
| 44 | + onClass: 'btn-primary', |
| 45 | + onstyle: '', /* for backward compatibility only */ |
45 | 46 | /** |
46 | 47 | * Type: string |
47 | 48 | * Default: "btn-default" |
48 | 49 | * Description: Class for "off" state from one of standard bootstrap button types. |
49 | 50 | * Possible values: btn-default, btn-primary,btn- success, btn-info, btn-warning, btn-danger |
50 | 51 | */ |
51 | | - offstyle: 'btn-default', |
| 52 | + offClass: 'btn-default', |
| 53 | + offstyle: '', /* for some backward compatibility only */ |
52 | 54 | /** |
53 | 55 | * Type: JSON string |
54 | 56 | * Default: '' |
|
77 | 79 | * manipulates this attribute, plus there is additional code that propagates its value to child elements. |
78 | 80 | * Applying "disabled" to <toggle> itself apparently does nothing, but when its value is propagated to |
79 | 81 | * two child <label> elements, it allows us to disable the widget. |
80 | | - * Note that attribute "diasbled" is not the same as ng-disabled Angular directive. In most cases, you should |
81 | | - * use <toggle ... ng-disabled="expression"> (not <toggle ... disabled="{{expression}}">) for this to work |
82 | | - * properly. |
83 | | - * [Per HTML specs, the "disabled" property does not need a value. Just mentioning it is enough. Angular will, |
84 | | - * however, also add the value "disabled" (< ... disabled="disabled">)] |
| 82 | + * Note that attribute "diasbled" is not the same as ng-disabled Angular directive. In most cases, you |
| 83 | + * should use <toggle ... ng-disabled="expression"> (not <toggle ... disabled="{{expression}}">) for this |
| 84 | + * to work properly. |
| 85 | + * [Per HTML specs, the "disabled" property does not need a value. Just mentioning it is enough. Angular |
| 86 | + * will, however, also add the value "disabled" (< ... disabled="disabled">)] |
85 | 87 | */ |
86 | 88 | disabled: false, |
87 | 89 | }) |
|
91 | 93 | function ($scope, $attrs, $interpolate, $log, toggleConfig, $toggleSuppressError) { |
92 | 94 | var self = this; |
93 | 95 | var labels, spans, divs; |
94 | | - var ngModelCtrl = {$setViewValue: angular.noop}; |
| 96 | + var ngModelCtrl; |
95 | 97 | var toggleConfigKeys = Object.keys(toggleConfig); |
96 | 98 |
|
97 | 99 | // Configuration attributes |
98 | 100 | angular.forEach( toggleConfigKeys, function (k, i) { |
99 | 101 | if (angular.isDefined($attrs[k])) { |
100 | | - /* |
101 | | - if (i < toggleConfigKeys.length) { |
102 | | - self[k] = $interpolate($attrs[k])($scope.$parent); |
103 | | - } else { |
104 | | - self[k] = $scope.$parent.$eval($attrs[k]); |
105 | | - } |
106 | | - */ |
107 | 102 | switch ( typeof toggleConfig[k] ) { |
108 | 103 | case 'string': |
109 | 104 | self[k] = $interpolate($attrs[k])($scope.$parent); |
|
119 | 114 | } |
120 | 115 | }); |
121 | 116 |
|
| 117 | + // Special treatment for onstyle and offstyle, now deprecated attributes: |
| 118 | + // If set, we will use their values for onClass and offClass respectively |
| 119 | + if (self.onstyle) { |
| 120 | + self.onClass = self.onstyle; |
| 121 | + } |
| 122 | + if (self.offstyle) { |
| 123 | + self.offClass = self.offstyle; |
| 124 | + } |
| 125 | + |
122 | 126 | this.init = function (ngModelCtrl_) { |
123 | 127 | ngModelCtrl = ngModelCtrl_; |
124 | 128 |
|
125 | | - labels = self.element.find('label'); |
126 | | - spans = self.element.find('span'); |
127 | | - divs = self.element.find('div'); |
128 | | - // ^-- divs[0] is the DIV that has class="toggle btn" |
129 | | - // divs[1] is a child of [0] and has class="toggle-group" |
| 129 | + var labels = self.element.find('label'); |
| 130 | + var spans = self.element.find('span'); |
| 131 | + var divs = self.element.find('div'); |
| 132 | + |
| 133 | + self.wrapperElement = divs[0]; |
| 134 | + self.onElement = labels[0]; |
| 135 | + self.offElement = labels[1]; |
| 136 | + self.handleElement = spans[0]; |
130 | 137 |
|
131 | 138 | // Set wigget's visible text such as On/Off or Enable/Disable |
132 | | - angular.element(labels[0]).html(self.on); |
133 | | - angular.element(labels[1]).html(self.off); |
| 139 | + angular.element(self.onElement).html(self.on); |
| 140 | + angular.element(self.offElement).html(self.off); |
134 | 141 |
|
135 | 142 | self.computeStyle(); |
136 | 143 |
|
137 | 144 | ngModelCtrl.$render = function () { |
138 | 145 | self.toggle(); |
139 | | - } |
| 146 | + }; |
140 | 147 |
|
141 | 148 | // ng-change (for optional onChange event handler) |
142 | 149 | if (angular.isDefined($attrs.ngChange)) { |
|
152 | 159 | // The property must be propagated to lables and span inside the toggle-group container. This |
153 | 160 | // triggers .btn[disabled] style (cursor: not-allowed; opacity: 0.65;) but it does not prohibit |
154 | 161 | // the click event. Click event is handled in .onSwitch(). |
155 | | - angular.element(labels[0]).attr('disabled', self.disabled); |
156 | | - angular.element(labels[1]).attr('disabled', self.disabled); |
157 | | - angular.element( spans[0]).attr('disabled', self.disabled); |
| 162 | + angular.element(self.onElement).attr('disabled', self.disabled); |
| 163 | + angular.element(self.offElement).attr('disabled', self.disabled); |
| 164 | + angular.element(self.handleElement).attr('disabled', self.disabled); |
158 | 165 |
|
159 | 166 | // Build an object for widget's ng-style |
160 | 167 | $scope.wrapperStyle = (self.toggleStyle) ? $scope.$parent.$eval(self.toggleStyle) : {}; |
161 | 168 |
|
| 169 | + // Calculate the proper width |
162 | 170 | if (self.width) { |
163 | 171 | $scope.wrapperStyle.width = self.width; |
164 | 172 | } else { |
165 | | - // INCORRECT MATH - spans[0] overlaps two side-by-side LABEL's. Half of its width should not be included in the total. |
166 | | - //var wrapperComputedWidth = Math.max(labels[0].offsetWidth, labels[1].offsetWidth) + (spans[0].offsetWidth / 2); |
167 | | - var wrapperComputedWidth = Math.max(labels[0].offsetWidth, labels[1].offsetWidth); |
168 | | - var wrapperWidth = divs[0].offsetWidth; |
169 | | - |
170 | | - if (wrapperWidth < wrapperComputedWidth) { |
171 | | - $scope.wrapperStyle.width = wrapperComputedWidth + 'px'; |
172 | | - } else { |
173 | | - $scope.wrapperStyle.width = wrapperWidth + 'px'; |
174 | | - } |
| 173 | + var wrapperComputedWidth = Math.max( |
| 174 | + self.onElement.scrollWidth, |
| 175 | + self.offElement.scrollWidth) + 12; |
| 176 | + $scope.wrapperStyle.width = wrapperComputedWidth + 'px'; |
175 | 177 | } |
176 | 178 |
|
| 179 | + // Calculate the proper height |
177 | 180 | if (self.height) { |
178 | 181 | $scope.wrapperStyle.height = self.height; |
179 | 182 | } else { |
180 | | - var wrapperComputedHeight = Math.max(labels[0].offsetHeight, labels[1].offsetHeight); |
181 | | - var wrapperHeight = divs[1].offsetHeight; |
| 183 | + var wrapperComputedHeight = Math.max( |
| 184 | + self.onElement.offsetHeight, |
| 185 | + self.offElement.offsetHeight); |
| 186 | + var wrapperHeight = self.wrapperElement.offsetHeight; |
182 | 187 |
|
183 | | - if (wrapperHeight < wrapperComputedHeight && self.size !== 'btn-xs' && self.size !== 'btn-sm') { |
184 | | - $scope.wrapperStyle.height = wrapperComputedHeight + 'px'; |
| 188 | + if (wrapperHeight < wrapperComputedHeight && |
| 189 | + self.size !== 'btn-xs' && self.size !== 'btn-sm') { |
| 190 | + $scope.wrapperStyle.height = wrapperComputedHeight + 'px'; |
185 | 191 | } else { |
186 | 192 | $scope.wrapperStyle.height = wrapperHeight + 'px'; |
187 | 193 | } |
188 | 194 | } |
189 | 195 |
|
190 | 196 | // Build arrays that will be passed to widget's ng-class. |
191 | | - $scope.onClass = [self.onstyle , self.size, 'toggle-on']; |
192 | | - $scope.offClass = [self.offstyle, self.size, 'toggle-off']; |
193 | | - $scope.handleClass = [self.size , 'toggle-handle']; |
| 197 | + $scope.onComputedClass = [self.onClass , self.size, 'toggle-on']; |
| 198 | + $scope.offComputedClass = [self.offClass, self.size, 'toggle-off']; |
| 199 | + $scope.handleComputedClass = [self.size , 'toggle-handle']; |
194 | 200 | }; |
195 | 201 |
|
196 | 202 | this.toggle = function () { |
197 | | - if (angular.isDefined(ngModelCtrl.$viewValue)) { |
198 | | - if (ngModelCtrl.$viewValue) { |
199 | | - $scope.wrapperClass = [self.onstyle, self.size, self.style]; |
200 | | - } else { |
201 | | - $scope.wrapperClass = [self.offstyle, 'off ', self.size, self.style]; |
202 | | - } |
203 | | - } else { |
204 | | - $scope.wrapperClass = [self.offstyle, 'off ', self.size, self.style]; |
205 | | - } |
| 203 | + if (ngModelCtrl.$viewValue) { |
| 204 | + angular.element(self.wrapperElement). |
| 205 | + removeClass('off ' + self.offClass). |
| 206 | + addClass(self.onClass); |
| 207 | + } else { |
| 208 | + angular.element(self.wrapperElement). |
| 209 | + addClass('off ' + self.offClass). |
| 210 | + removeClass(self.onClass); |
| 211 | + } |
206 | 212 | }; |
207 | 213 |
|
208 | 214 | $scope.onSwitch = function (evt) { |
209 | | - if (self.disabled) { // prevent changing .$viewValue if .disabled == true |
| 215 | + if (self.disabled) { // prevent changing .$viewValue if .disabled == true |
210 | 216 | return false; |
211 | | - } else { |
212 | | - ngModelCtrl.$setViewValue(!ngModelCtrl.$viewValue); |
213 | | - ngModelCtrl.$render(); |
214 | | - } |
215 | | - return true; |
| 217 | + } else { |
| 218 | + ngModelCtrl.$setViewValue(!ngModelCtrl.$viewValue); |
| 219 | + ngModelCtrl.$render(); |
| 220 | + } |
| 221 | + return true; |
216 | 222 | }; |
217 | 223 |
|
218 | | - // Watchable data attributes |
219 | | - angular.forEach(['ngModel'], function (key) { |
220 | | - var watch = $scope.$parent.$watch($attrs[key], function (value) { |
221 | | - ngModelCtrl.$render(); |
222 | | - }); |
223 | | - $scope.$parent.$on('$destroy', function () { |
224 | | - watch(); |
225 | | - }); |
226 | | - }); |
227 | | - |
228 | 224 | angular.forEach( toggleConfigKeys, function (k, i) { |
229 | 225 | $attrs.$observe(k, function (v) { |
230 | 226 | if (self[k] !== v) { |
|
238 | 234 | .directive('toggle', function () { |
239 | 235 | return { |
240 | 236 | restrict: 'E', |
241 | | - transclude: true, |
242 | | - template: '<div class="toggle btn" ng-class="wrapperClass" ng-style="wrapperStyle" ng-click="onSwitch($event)">' + |
243 | | - '<div class="toggle-group">' + |
244 | | - '<label class="btn" ng-class="onClass"></label>' + |
245 | | - '<label class="btn active" ng-class="offClass"></label>' + |
246 | | - '<span class="btn btn-default" ng-class="handleClass"></span>' + |
247 | | - '</div>' + |
248 | | - '</div>', |
| 237 | + template: '<div ng-cloak class="toggle btn off" ng-style="wrapperStyle"' + |
| 238 | + 'ng-click="onSwitch($event)">' + |
| 239 | + '<div class="toggle-group">' + |
| 240 | + '<label class="btn" ng-class="onComputedClass"></label>' + |
| 241 | + '<label class="btn active" ng-class="offComputedClass"></label>' + |
| 242 | + '<span class="btn btn-default" ng-class="handleComputedClass"></span>' + |
| 243 | + '</div>' + |
| 244 | + '</div>', |
249 | 245 | scope: { |
250 | 246 | ngModel: '=' |
251 | 247 | }, |
|
254 | 250 | controllerAs: 'toggle', |
255 | 251 | compile: function (element, attrs, transclude) { |
256 | 252 | return { |
257 | | - pre: function (scope, element, attrs, ctrls) { |
| 253 | + post: function (scope, element, attrs, ctrls) { |
258 | 254 | var toggleCtrl = ctrls[0], ngModelCtrl = ctrls[1]; |
259 | 255 | toggleCtrl.element = element; |
260 | 256 | toggleCtrl.init(ngModelCtrl); |
261 | 257 | }, |
262 | | - post: function () {} |
263 | | - } |
| 258 | + pre: function () {} |
| 259 | + }; |
264 | 260 | } |
265 | 261 | }; |
266 | 262 | } |
|
0 commit comments