|
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