|
135 | 135 |
|
136 | 136 | ctrl.parseOptions = function( model ) {
|
137 | 137 | var currPromise;
|
138 |
| - $scope.$watch( model, watchFn, true ); |
139 |
| - $scope.$watch( model, function( newValue, oldValue ) { |
140 |
| - var isFn = ng.isFunction; |
141 |
| - var isPromise = !!newValue && isFn( newValue.then ); |
142 |
| - isPromise &= !!oldValue && isFn( oldValue.then ); |
143 |
| - |
144 |
| - // We won't handle anything here unless both values are promises. |
145 |
| - if ( !ng.equals( newValue, oldValue ) || !isPromise ) { |
146 |
| - return; |
147 |
| - } |
148 |
| - |
149 |
| - watchFn( newValue ); |
150 |
| - }); |
151 |
| - |
152 |
| - function watchFn( value ) { |
| 138 | + $scope.$watch( model, function watchFn( value ) { |
153 | 139 | var promise;
|
154 | 140 | currPromise = null;
|
155 | 141 |
|
|
174 | 160 |
|
175 | 161 | currPromise = promise;
|
176 | 162 | }
|
177 |
| - } |
| 163 | + }, true ); |
178 | 164 | };
|
179 | 165 |
|
180 | 166 | function clearSearch() {
|
|
204 | 190 | ]);
|
205 | 191 |
|
206 | 192 | module.directive( "dropdownOptions", [
|
| 193 | + "$compile", |
207 | 194 | "repeatParser",
|
208 | 195 | "dropdownConfig",
|
209 |
| - function( repeatParser, dropdownConfig ) { |
| 196 | + function( $compile, repeatParser, dropdownConfig ) { |
210 | 197 | var definition = {};
|
211 | 198 |
|
212 | 199 | definition.restrict = "EA";
|
|
215 | 202 | definition.templateUrl = "templates/dropdown/options.html";
|
216 | 203 | definition.require = "^dropdown";
|
217 | 204 |
|
218 |
| - definition.compile = function( tElement, tAttr ) { |
219 |
| - var model; |
220 |
| - var option = tElement.querySelector( ".dropdown-option" ); |
221 |
| - var repeat = repeatParser.parse( tAttr.items ); |
222 |
| - |
223 |
| - if ( !repeat ) { |
| 205 | + definition.compile = function( tElement ) { |
| 206 | + // When in a detached case, we won't let compile go ahead |
| 207 | + if ( !tElement.parent().length ) { |
224 | 208 | return;
|
225 | 209 | }
|
226 | 210 |
|
227 |
| - model = repeat.expr; |
228 |
| - repeat.expr = "$dropdown.options"; |
229 |
| - |
230 |
| - option.attr( "ng-repeat", repeatParser.toNgRepeat( repeat ) ); |
231 |
| - option.attr( "ng-click", "$dropdown.addItem( " + repeat.item + " )" ); |
232 |
| - option.attr( "ng-class", "{" + |
233 |
| - "active: $dropdown.activeOption === " + repeat.item + |
234 |
| - "}" ); |
235 |
| - |
236 |
| - return function( scope, element, attr, $dropdown ) { |
237 |
| - var list = element[ 0 ]; |
238 |
| - configureOverflow(); |
239 |
| - |
240 |
| - $dropdown.parseOptions( model ); |
241 |
| - $dropdown.valueKey = tAttr.value || null; |
242 |
| - |
243 |
| - scope.$watch( "$dropdown.open", adjustScroll ); |
244 |
| - scope.$watch( "$dropdown.activeOption", adjustScroll ); |
245 |
| - |
246 |
| - function adjustScroll() { |
247 |
| - var fromScrollTop, index, activeElem; |
248 |
| - var options = $dropdown.options; |
249 |
| - var scrollTop = list.scrollTop; |
250 |
| - |
251 |
| - if ( ng.isArray( options ) ) { |
252 |
| - index = options.indexOf( $dropdown.activeOption ); |
253 |
| - } else { |
254 |
| - // To keep compatibility with arrays, we'll init the index as -1 |
255 |
| - index = -1; |
256 |
| - Object.keys( options ).some(function( key, i ) { |
257 |
| - if ( options[ key ] === $dropdown.activeOption ) { |
258 |
| - index = i; |
259 |
| - return true; |
260 |
| - } |
261 |
| - }); |
262 |
| - } |
| 211 | + return definition.link; |
| 212 | + }; |
263 | 213 |
|
264 |
| - activeElem = list.querySelectorAll( ".dropdown-option" )[ index ]; |
| 214 | + definition.link = function( scope, element, attr, $dropdown, transclude ) { |
| 215 | + var list = element[ 0 ]; |
| 216 | + var option = element.querySelector( ".dropdown-option" ); |
| 217 | + var repeat = repeatParser.parse( attr.items ); |
| 218 | + |
| 219 | + // If we have a repeat expr, let's use it to build the option list |
| 220 | + if ( repeat ) { |
| 221 | + $dropdown.parseOptions( repeat.expr ); |
| 222 | + repeat.expr = "$dropdown.options"; |
| 223 | + |
| 224 | + // Option list building |
| 225 | + transclude(function( childs ) { |
| 226 | + option.append( childs ); |
| 227 | + }); |
| 228 | + |
| 229 | + // Add a few directives to the option... |
| 230 | + option.attr( "ng-repeat", repeatParser.toNgRepeat( repeat ) ); |
| 231 | + option.attr( "ng-click", "$dropdown.addItem( " + repeat.item + " )" ); |
| 232 | + option.attr( "ng-class", "{" + |
| 233 | + "active: $dropdown.activeOption === " + repeat.item + |
| 234 | + "}" ); |
| 235 | + |
| 236 | + // ...and compile it |
| 237 | + $compile( option )( scope ); |
| 238 | + } |
265 | 239 |
|
266 |
| - if ( !$dropdown.open || !activeElem ) { |
267 |
| - // To be handled! |
268 |
| - return; |
269 |
| - } |
| 240 | + // Configure the overflow for this list |
| 241 | + configureOverflow(); |
270 | 242 |
|
271 |
| - fromScrollTop = activeElem.offsetTop - list.scrollTop; |
272 |
| - |
273 |
| - // If the option is above the current scroll, we'll make it appear on the |
274 |
| - // top of the scroll. |
275 |
| - // Otherwise, it'll appear in the end of the scroll view. |
276 |
| - if ( fromScrollTop < 0 ) { |
277 |
| - scrollTop = activeElem.offsetTop; |
278 |
| - } else if ( list.clientHeight <= fromScrollTop + activeElem.clientHeight ) { |
279 |
| - scrollTop = activeElem.offsetTop + |
280 |
| - activeElem.clientHeight - |
281 |
| - list.clientHeight; |
282 |
| - } |
| 243 | + // Set the value key |
| 244 | + $dropdown.valueKey = attr.value || null; |
283 | 245 |
|
284 |
| - list.scrollTop = scrollTop; |
285 |
| - } |
| 246 | + // Scope Watches |
| 247 | + // --------------------------------------------------------------------------------- |
| 248 | + scope.$watch( "$dropdown.open", adjustScroll ); |
| 249 | + scope.$watch( "$dropdown.activeOption", adjustScroll ); |
| 250 | + |
| 251 | + // Functions |
| 252 | + // --------------------------------------------------------------------------------- |
| 253 | + function adjustScroll() { |
| 254 | + var fromScrollTop, index, activeElem; |
| 255 | + var options = $dropdown.options; |
| 256 | + var scrollTop = list.scrollTop; |
286 | 257 |
|
287 |
| - function configureOverflow() { |
288 |
| - var height; |
289 |
| - var view = list.ownerDocument.defaultView; |
290 |
| - var styles = view.getComputedStyle( list, null ); |
291 |
| - var display = element.css( "display" ); |
292 |
| - var size = dropdownConfig.optionsPageSize; |
293 |
| - var li = $( "<li class='dropdown-option'> </li>" )[ 0 ]; |
294 |
| - element.prepend( li ); |
295 |
| - |
296 |
| - // Temporarily show the element, just to calculate the li height |
297 |
| - element.css( "display", "block" ); |
298 |
| - |
299 |
| - // Calculate the height, considering border/padding |
300 |
| - height = li.clientHeight * size; |
301 |
| - height = [ "padding", "border" ].reduce(function( value, prop ) { |
302 |
| - var top = styles.getPropertyValue( prop + "-top" ) || ""; |
303 |
| - var bottom = styles.getPropertyValue( prop + "-bottom" ) || ""; |
304 |
| - |
305 |
| - value += +top.replace( "px", "" ) || 0; |
306 |
| - value += +bottom.replace( "px", "" ) || 0; |
307 |
| - |
308 |
| - return value; |
309 |
| - }, height ); |
310 |
| - |
311 |
| - // Set overflow CSS rules |
312 |
| - element.css({ |
313 |
| - "overflow-y": "auto", |
314 |
| - "max-height": height + "px" |
| 258 | + if ( ng.isArray( options ) ) { |
| 259 | + index = options.indexOf( $dropdown.activeOption ); |
| 260 | + } else { |
| 261 | + // To keep compatibility with arrays, we'll init the index as -1 |
| 262 | + index = -1; |
| 263 | + Object.keys( options ).some(function( key, i ) { |
| 264 | + if ( options[ key ] === $dropdown.activeOption ) { |
| 265 | + index = i; |
| 266 | + return true; |
| 267 | + } |
315 | 268 | });
|
| 269 | + } |
316 | 270 |
|
317 |
| - // And finally, set the element display to the previous value |
318 |
| - element.css( "display", display ); |
| 271 | + activeElem = list.querySelectorAll( ".dropdown-option" )[ index ]; |
319 | 272 |
|
320 |
| - // Also remove the dummy <li> created previously |
321 |
| - $( li ).remove(); |
| 273 | + if ( !$dropdown.open || !activeElem ) { |
| 274 | + // To be handled! |
| 275 | + return; |
| 276 | + } |
| 277 | + |
| 278 | + fromScrollTop = activeElem.offsetTop - list.scrollTop; |
| 279 | + |
| 280 | + // If the option is above the current scroll, we'll make it appear on the |
| 281 | + // top of the scroll. |
| 282 | + // Otherwise, it'll appear in the end of the scroll view. |
| 283 | + if ( fromScrollTop < 0 ) { |
| 284 | + scrollTop = activeElem.offsetTop; |
| 285 | + } else if ( list.clientHeight <= fromScrollTop + activeElem.clientHeight ) { |
| 286 | + scrollTop = activeElem.offsetTop + |
| 287 | + activeElem.clientHeight - |
| 288 | + list.clientHeight; |
322 | 289 | }
|
323 |
| - }; |
| 290 | + |
| 291 | + list.scrollTop = scrollTop; |
| 292 | + } |
| 293 | + |
| 294 | + function configureOverflow() { |
| 295 | + var height; |
| 296 | + var view = list.ownerDocument.defaultView; |
| 297 | + var styles = view.getComputedStyle( list, null ); |
| 298 | + var display = element.css( "display" ); |
| 299 | + var size = dropdownConfig.optionsPageSize; |
| 300 | + var li = $( "<li class='dropdown-option'> </li>" )[ 0 ]; |
| 301 | + element.prepend( li ); |
| 302 | + |
| 303 | + // Temporarily show the element, just to calculate the li height |
| 304 | + element.css( "display", "block" ); |
| 305 | + |
| 306 | + // Calculate the height, considering border/padding |
| 307 | + height = li.clientHeight * size; |
| 308 | + height = [ "padding", "border" ].reduce(function( value, prop ) { |
| 309 | + var top = styles.getPropertyValue( prop + "-top" ) || ""; |
| 310 | + var bottom = styles.getPropertyValue( prop + "-bottom" ) || ""; |
| 311 | + |
| 312 | + value += +top.replace( "px", "" ) || 0; |
| 313 | + value += +bottom.replace( "px", "" ) || 0; |
| 314 | + |
| 315 | + return value; |
| 316 | + }, height ); |
| 317 | + |
| 318 | + // Set overflow CSS rules |
| 319 | + element.css({ |
| 320 | + "overflow-y": "auto", |
| 321 | + "max-height": height + "px" |
| 322 | + }); |
| 323 | + |
| 324 | + // And finally, set the element display to the previous value |
| 325 | + element.css( "display", display ); |
| 326 | + |
| 327 | + // Also remove the dummy <li> created previously |
| 328 | + $( li ).remove(); |
| 329 | + } |
324 | 330 | };
|
325 | 331 |
|
326 | 332 | return definition;
|
|
0 commit comments