@@ -29,10 +29,11 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
29
29
} ;
30
30
} ] )
31
31
32
- . directive ( 'typeahead' , [ '$compile' , '$parse' , '$q' , '$timeout' , '$document' , '$rootScope' , '$position' , 'typeaheadParser' ,
33
- function ( $compile , $parse , $q , $timeout , $document , $rootScope , $position , typeaheadParser ) {
32
+ . directive ( 'typeahead' , [ '$compile' , '$parse' , '$q' , '$timeout' , '$document' , '$window' , '$ rootScope', '$position' , 'typeaheadParser' ,
33
+ function ( $compile , $parse , $q , $timeout , $document , $window , $rootScope , $position , typeaheadParser ) {
34
34
35
35
var HOT_KEYS = [ 9 , 13 , 27 , 38 , 40 ] ;
36
+ var eventDebounceTime = 200 ;
36
37
37
38
return {
38
39
require :'ngModel' ,
@@ -96,6 +97,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
96
97
matches : 'matches' ,
97
98
active : 'activeIdx' ,
98
99
select : 'select(activeIdx)' ,
100
+ 'move-in-progress' : 'moveInProgress' ,
99
101
query : 'query' ,
100
102
position : 'position'
101
103
} ) ;
@@ -153,8 +155,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
153
155
//position pop-up with matches - we need to re-calculate its position each time we are opening a window
154
156
//with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page
155
157
//due to other elements being rendered
156
- scope . position = appendToBody ? $position . offset ( element ) : $position . position ( element ) ;
157
- scope . position . top = scope . position . top + element . prop ( 'offsetHeight' ) ;
158
+ recalculatePosition ( ) ;
158
159
159
160
element . attr ( 'aria-expanded' , true ) ;
160
161
} else {
@@ -170,6 +171,48 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
170
171
} ) ;
171
172
} ;
172
173
174
+ // bind events only if appendToBody params exist - performance feature
175
+ if ( appendToBody ) {
176
+ angular . element ( $window ) . bind ( 'resize' , fireRecalculating ) ;
177
+ $document . find ( 'body' ) . bind ( 'scroll' , fireRecalculating ) ;
178
+ }
179
+
180
+ // Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
181
+ var timeoutEventPromise ;
182
+
183
+ // Default progress type
184
+ scope . moveInProgress = false ;
185
+
186
+ function fireRecalculating ( ) {
187
+ if ( ! scope . moveInProgress ) {
188
+ scope . moveInProgress = true ;
189
+ scope . $digest ( ) ;
190
+ }
191
+
192
+ // Cancel previous timeout
193
+ if ( timeoutEventPromise ) {
194
+ $timeout . cancel ( timeoutEventPromise ) ;
195
+ }
196
+
197
+ // Debounced executing recalculate after events fired
198
+ timeoutEventPromise = $timeout ( function ( ) {
199
+ // if popup is visible
200
+ if ( scope . matches . length ) {
201
+ recalculatePosition ( ) ;
202
+ }
203
+
204
+ scope . moveInProgress = false ;
205
+ scope . $digest ( ) ;
206
+ } , eventDebounceTime ) ;
207
+ }
208
+
209
+ // recalculate actual position and set new values to scope
210
+ // after digest loop is popup in right position
211
+ function recalculatePosition ( ) {
212
+ scope . position = appendToBody ? $position . offset ( element ) : $position . position ( element ) ;
213
+ scope . position . top += element . prop ( 'offsetHeight' ) ;
214
+ }
215
+
173
216
resetMatches ( ) ;
174
217
175
218
//we need to propagate user's query so we can higlight matches
@@ -358,6 +401,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
358
401
query :'=' ,
359
402
active :'=' ,
360
403
position :'&' ,
404
+ moveInProgress :'=' ,
361
405
select :'&'
362
406
} ,
363
407
replace :true ,
0 commit comments