This repository was archived by the owner on Sep 5, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
/
Copy pathautocompleteDirective.js
577 lines (555 loc) · 27.6 KB
/
autocompleteDirective.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
angular
.module('material.components.autocomplete')
.directive('mdAutocomplete', MdAutocomplete);
/**
* @ngdoc directive
* @name mdAutocomplete
* @module material.components.autocomplete
*
* @description
* `<md-autocomplete>` is a special input component with a drop-down of all possible matches to a
* custom query. This component allows you to provide real-time suggestions as the user types
* in the input area.
*
* To start, you will need to specify the required parameters and provide a template for your
* results. The content inside `md-autocomplete` will be treated as a template.
*
* In more complex cases, you may want to include other content such as a message to display when
* no matches were found. You can do this by wrapping your template in `md-item-template` and
* adding a tag for `md-not-found`. An example of this is shown below.
*
* To reset the displayed value you must clear both values for `md-search-text` and
* `md-selected-item`.
*
* ### Validation
*
* You can use `ng-messages` to include validation the same way that you would normally validate;
* however, if you want to replicate a standard input with a floating label, you will have to
* do the following:
*
* - Make sure that your template is wrapped in `md-item-template`
* - Add your `ng-messages` code inside of `md-autocomplete`
* - Add your validation properties to `md-autocomplete` (ie. `required`)
* - Add a `name` to `md-autocomplete` (to be used on the generated `input`)
*
* There is an example below of how this should look.
*
* ### Snapping Drop-Down
*
* You can cause the autocomplete drop-down to snap to an ancestor element by applying the
* `md-autocomplete-snap` attribute to that element. You can also snap to the width of
* the `md-autocomplete-snap` element by setting the attribute's value to `width`
* (ie. `md-autocomplete-snap="width"`).
*
* ### Notes
*
* **Autocomplete Dropdown Items Rendering**
*
* The `md-autocomplete` uses the the <a ng-href="api/directive/mdVirtualRepeat">
* mdVirtualRepeat</a> directive for displaying the results inside of the dropdown.<br/>
*
* > When encountering issues regarding the item template please take a look at the
* <a ng-href="api/directive/mdVirtualRepeatContainer">VirtualRepeatContainer</a> documentation.
*
* **Autocomplete inside of a Virtual Repeat**
*
* When using the `md-autocomplete` directive inside of a
* <a ng-href="api/directive/mdVirtualRepeatContainer">VirtualRepeatContainer</a> the dropdown items
* might not update properly, because caching of the results is enabled by default.
*
* The autocomplete will then show invalid dropdown items, because the Virtual Repeat only updates
* the scope bindings rather than re-creating the `md-autocomplete`. This means that the previous
* cached results will be used.
*
* > To avoid such problems, ensure that the autocomplete does not cache any results via
* `md-no-cache="true"`:
*
* <hljs lang="html">
* <md-autocomplete
* md-no-cache="true"
* md-selected-item="selectedItem"
* md-items="item in items"
* md-search-text="searchText"
* md-item-text="item.display">
* <span>{{ item.display }}</span>
* </md-autocomplete>
* </hljs>
*
*
* @param {expression} md-items An expression in the format of `item in results` to iterate over
* matches for your search.<br/><br/>
* The `results` expression can be also a function, which returns the results synchronously
* or asynchronously (per Promise).
* @param {expression=} md-selected-item-change An expression to be run each time a new item is
* selected.
* @param {expression=} md-search-text-change An expression to be run each time the search text
* updates.
* @param {expression=} md-search-text A model to bind the search query text to.
* @param {object=} md-selected-item A model to bind the selected item to.
* @param {expression=} md-item-text An expression that will convert your object to a single string.
* @param {string=} placeholder Placeholder text that will be forwarded to the input.
* @param {boolean=} md-no-cache Disables the internal caching that happens in autocomplete.
* @param {boolean=} ng-disabled Determines whether or not to disable the input field.
* @param {boolean=} md-require-match When set to true, the autocomplete will add a validator,
* which will evaluate to false, when no item is currently selected.
* @param {number=} md-min-length Specifies the minimum length of text before autocomplete will
* make suggestions.
* @param {number=} md-delay Specifies the amount of time (in milliseconds) to wait before looking
* for results.
* @param {boolean=} md-clear-button Whether the clear button for the autocomplete input should show
* up or not. When `md-floating-label` is set, defaults to false, defaults to true otherwise.
* @param {boolean=} md-autofocus If true, the autocomplete will be automatically focused when a
* `$mdDialog`, `$mdBottomsheet` or `$mdSidenav`, which contains the autocomplete, is opening.
* <br/><br/>
* Also the autocomplete will immediately focus the input element.
* @param {boolean=} md-no-asterisk When present, asterisk will not be appended to the floating
* label.
* @param {boolean=} md-autoselect If set to true, the first item will be automatically selected
* in the dropdown upon open.
* @param {string=} md-input-name The name attribute given to the input element to be used with
* FormController.
* @param {string=} md-menu-class This class will be applied to the dropdown menu for styling.
* @param {string=} md-menu-container-class This class will be applied to the parent container
* of the dropdown panel.
* @param {string=} md-input-class This will be applied to the input for styling. This attribute
* is only valid when a `md-floating-label` is defined.
* @param {string=} md-floating-label This will add a floating label to autocomplete and wrap it in
* `md-input-container`.
* @param {string=} md-select-on-focus When present the input's text will be automatically selected
* on focus.
* @param {string=} md-input-id An ID to be added to the input element.
* @param {number=} md-input-minlength The minimum length for the input's value for validation.
* @param {number=} md-input-maxlength The maximum length for the input's value for validation.
* @param {boolean=} md-select-on-match When set, autocomplete will automatically select
* the item if the search text is an exact match. <br/><br/>
* An exact match is when only one match is displayed.
* @param {boolean=} md-match-case-insensitive When set and using `md-select-on-match`, autocomplete
* will select on case-insensitive match.
* @param {string=} md-escape-options Override escape key logic. Default is `clear`.<br/>
* Options: `blur`, `clear`, `none`.
* @param {string=} md-dropdown-items Specifies the maximum amount of items to be shown in
* the dropdown.<br/><br/>
* When the dropdown doesn't fit into the viewport, the dropdown will shrink
* as much as possible.
* @param {string=} md-dropdown-position Overrides the default dropdown position. Options: `top`,
* `bottom`.
* @param {string=} input-aria-describedby A space-separated list of element IDs. This should
* contain the IDs of any elements that describe this autocomplete. Screen readers will read the
* content of these elements at the end of announcing that the autocomplete has been selected
* and describing its current state. The descriptive elements do not need to be visible on the
* page.
* @param {string=} input-aria-labelledby A space-separated list of element IDs. The ideal use case
* is that this would contain the ID of a `<label>` element that is associated with this
* autocomplete. This will only have affect when `md-floating-label` is not defined.<br><br>
* For `<label id="state">US State</label>`, you would set this to
* `input-aria-labelledby="state"`.
* @param {string=} input-aria-label A label that will be applied to the autocomplete's input.
* This will be announced by screen readers before the placeholder.
* This will only have affect when `md-floating-label` is not defined. If you define both
* `input-aria-label` and `input-aria-labelledby`, then `input-aria-label` will take precedence.
* @param {string=} md-selected-message Attribute to specify the text that the screen reader will
* announce after a value is selected. Default is: "selected". If `Alaska` is selected in the
* options panel, it will read "Alaska selected". You will want to override this when your app
* runs in a non-English locale.
* @param {string=} md-no-match-message Attribute to specify the text that the screen reader will
* announce after a query returns no matching results.
* Default is: "There are no matches available.". You will want to override this when your app
* runs in a non-English locale.
* @param {string=} md-single-match-message Attribute to specify the text that the screen reader
* will announce after a query returns a single matching result.
* Default is: "There is 1 match available.". You will want to override this when your app
* runs in a non-English locale.
* @param {string=} md-multiple-match-start-message Attribute to specify the text that the screen
* reader will announce after a query returns multiple matching results. The number of matching
* results will be read after this text. Default is: "There are ". You will want to override this
* when your app runs in a non-English locale.
* @param {string=} md-multiple-match-end-message Attribute to specify the text that the screen
* reader will announce after a query returns multiple matching results. The number of matching
* results will be read before this text. Default is: " matches available.". You will want to
* override this when your app runs in a non-English locale.
* @param {boolean=} ng-trim If set to false, the search text will be not trimmed automatically.
* Defaults to true.
* @param {string=} ng-pattern Adds the pattern validator to the ngModel of the search text.
* See the [ngPattern Directive](https://docs.angularjs.org/api/ng/directive/ngPattern)
* for more details.
* @param {string=} md-mode Specify the repeat mode for suggestion lists. Acceptable values include
* `virtual` (md-virtual-repeat) and `standard` (ng-repeat). See the
* `Specifying Repeat Mode` example for mode details. Default is `virtual`.
*
* @usage
* ### Basic Example
* <hljs lang="html">
* <md-autocomplete
* md-selected-item="selectedItem"
* md-search-text="searchText"
* md-items="item in getMatches(searchText)"
* md-item-text="item.display">
* <span md-highlight-text="searchText">{{item.display}}</span>
* </md-autocomplete>
* </hljs>
*
* ### Example with "not found" message
* <hljs lang="html">
* <md-autocomplete
* md-selected-item="selectedItem"
* md-search-text="searchText"
* md-items="item in getMatches(searchText)"
* md-item-text="item.display">
* <md-item-template>
* <span md-highlight-text="searchText">{{item.display}}</span>
* </md-item-template>
* <md-not-found>
* No matches found.
* </md-not-found>
* </md-autocomplete>
* </hljs>
*
* In this example, our code utilizes `md-item-template` and `md-not-found` to specify the
* different parts that make up our component.
*
* ### Clear button for the input
* By default, the clear button is displayed when there is input. This aligns with the spec's
* [Search Pattern](https://material.io/archive/guidelines/patterns/search.html#search-in-app-search).
* In floating label mode, when `md-floating-label="My Label"` is applied, the clear button is not
* displayed by default (see the spec's
* [Autocomplete Text Field](https://material.io/archive/guidelines/components/text-fields.html#text-fields-layout)).
*
* Nevertheless, developers are able to explicitly toggle the clear button for all autocomplete
* components with `md-clear-button`.
*
* <hljs lang="html">
* <md-autocomplete ... md-clear-button="true"></md-autocomplete>
* <md-autocomplete ... md-clear-button="false"></md-autocomplete>
* </hljs>
*
* In previous versions, the clear button was always hidden when the component was disabled.
* This changed in `1.1.5` to give the developer control of this behavior. This example
* will hide the clear button only when the component is disabled.
*
* <hljs lang="html">
* <md-autocomplete ... ng-disabled="disabled" md-clear-button="!disabled"></md-autocomplete>
* </hljs>
*
* ### Example with validation
* <hljs lang="html">
* <form name="autocompleteForm">
* <md-autocomplete
* required
* md-input-name="autocomplete"
* md-selected-item="selectedItem"
* md-search-text="searchText"
* md-items="item in getMatches(searchText)"
* md-item-text="item.display">
* <md-item-template>
* <span md-highlight-text="searchText">{{item.display}}</span>
* </md-item-template>
* <div ng-messages="autocompleteForm.autocomplete.$error">
* <div ng-message="required">This field is required</div>
* </div>
* </md-autocomplete>
* </form>
* </hljs>
*
* In this example, our code utilizes `md-item-template` and `ng-messages` to specify
* input validation for the field.
*
* ### Asynchronous Results
* The autocomplete items expression also supports promises, which will resolve with the query
* results.
*
* <hljs lang="js">
* function AppController($scope, $http) {
* $scope.query = function(searchText) {
* return $http
* .get(BACKEND_URL + '/items/' + searchText)
* .then(function(data) {
* // Map the response object to the data object.
* return data;
* });
* };
* }
* </hljs>
*
* <hljs lang="html">
* <md-autocomplete
* md-selected-item="selectedItem"
* md-search-text="searchText"
* md-items="item in query(searchText)">
* <md-item-template>
* <span md-highlight-text="searchText">{{item}}</span>
* </md-item-template>
* </md-autocomplete>
* </hljs>
*
* ### Specifying Repeat Mode
* You can use `md-mode` to specify whether to use standard or virtual lists for
* rendering autocomplete options.
* The `md-mode` accepts two values:
* - `virtual` (default) Uses `md-virtual-repeat` to render list items. Virtual
* mode requires you to have consistent heights for all suggestions.
* - `standard` uses `ng-repeat` to render list items. This allows you to have
* options of varying heights.
*
* Note that using 'standard' mode will require you to address any list
* performance issues (e.g. pagination) separately within your application.
*
* <hljs lang="html">
* <md-autocomplete
* md-selected-item="selectedItem"
* md-search-text="searchText"
* md-items="item in getMatches(searchText)"
* md-item-text="item.display"
* md-mode="standard">
* <span md-highlight-text="searchText">{{item.display}}</span>
* </md-autocomplete>
* </hljs>
*/
function MdAutocomplete ($$mdSvgRegistry) {
var REPEAT_STANDARD = 'standard';
var REPEAT_VIRTUAL = 'virtual';
var REPEAT_MODES = [REPEAT_STANDARD, REPEAT_VIRTUAL];
/** get a valid repeat mode from an md-mode attribute string. */
function getRepeatMode(modeStr) {
if (!modeStr) { return REPEAT_VIRTUAL; }
modeStr = modeStr.toLowerCase();
return REPEAT_MODES.indexOf(modeStr) > -1 ? modeStr : REPEAT_VIRTUAL;
}
return {
controller: 'MdAutocompleteCtrl',
controllerAs: '$mdAutocompleteCtrl',
scope: {
inputName: '@mdInputName',
inputMinlength: '@mdInputMinlength',
inputMaxlength: '@mdInputMaxlength',
searchText: '=?mdSearchText',
selectedItem: '=?mdSelectedItem',
itemsExpr: '@mdItems',
itemText: '&mdItemText',
placeholder: '@placeholder',
inputAriaDescribedBy: '@?inputAriaDescribedby',
inputAriaLabelledBy: '@?inputAriaLabelledby',
inputAriaLabel: '@?inputAriaLabel',
noCache: '=?mdNoCache',
requireMatch: '=?mdRequireMatch',
selectOnMatch: '=?mdSelectOnMatch',
matchInsensitive: '=?mdMatchCaseInsensitive',
itemChange: '&?mdSelectedItemChange',
textChange: '&?mdSearchTextChange',
minLength: '=?mdMinLength',
delay: '=?mdDelay',
autofocus: '=?mdAutofocus',
floatingLabel: '@?mdFloatingLabel',
autoselect: '=?mdAutoselect',
menuClass: '@?mdMenuClass',
menuContainerClass: '@?mdMenuContainerClass',
inputClass: '@?mdInputClass',
inputId: '@?mdInputId',
escapeOptions: '@?mdEscapeOptions',
dropdownItems: '=?mdDropdownItems',
dropdownPosition: '@?mdDropdownPosition',
clearButton: '=?mdClearButton',
selectedMessage: '@?mdSelectedMessage',
noMatchMessage: '@?mdNoMatchMessage',
singleMatchMessage: '@?mdSingleMatchMessage',
multipleMatchStartMessage: '@?mdMultipleMatchStartMessage',
multipleMatchEndMessage: '@?mdMultipleMatchEndMessage',
mdMode: '=?mdMode'
},
compile: function(tElement, tAttrs) {
var attributes = ['md-select-on-focus', 'md-no-asterisk', 'ng-trim', 'ng-pattern'];
var input = tElement.find('input');
attributes.forEach(function(attribute) {
var attrValue = tAttrs[tAttrs.$normalize(attribute)];
if (attrValue !== null) {
input.attr(attribute, attrValue);
}
});
return function(scope, element, attrs, ctrl) {
// Retrieve the state of using a md-not-found template by using our attribute, which will
// be added to the element in the template function.
ctrl.hasNotFound = !!element.attr('md-has-not-found');
// By default the inset autocomplete should show the clear button when not explicitly
// overwritten or in floating label mode.
if (!angular.isDefined(attrs.mdClearButton) && !scope.floatingLabel) {
scope.clearButton = true;
}
scope.mdMode = getRepeatMode(attrs.mdMode);
// Stop click events from bubbling up to the document and triggering a flicker of the
// options panel while still supporting ng-click to be placed on md-autocomplete.
element.on('click touchstart touchend', function(event) {
event.stopPropagation();
});
};
},
template: function (element, attr) {
var noItemsTemplate = getNoItemsTemplate(),
itemTemplate = getItemTemplate(),
leftover = element.html(),
tabindex = attr.tabindex;
// Set our attribute for the link function above which runs later.
// We will set an attribute, because otherwise the stored variables will be trashed when
// removing the element is hidden while retrieving the template. For example when using ngIf.
if (noItemsTemplate) element.attr('md-has-not-found', true);
// Always set our tabindex of the autocomplete directive to -1, because our input
// will hold the actual tabindex.
element.attr('tabindex', '-1');
return '\
<md-autocomplete-wrap\
ng-class="{ \'md-whiteframe-z1\': !floatingLabel, \
\'md-menu-showing\': !$mdAutocompleteCtrl.hidden, \
\'md-show-clear-button\': !!clearButton }">\
' + getInputElement() + '\
' + getClearButton() + '\
<md-progress-linear\
class="' + (attr.mdFloatingLabel ? 'md-inline' : '') + '"\
ng-if="$mdAutocompleteCtrl.loadingIsVisible()"\
md-mode="indeterminate"></md-progress-linear>\
' + getContainer(attr.mdMenuContainerClass, attr.mdMode) + '\
<ul class="md-autocomplete-suggestions"\
ng-class="::menuClass"\
id="ul-{{$mdAutocompleteCtrl.id}}"\
ng-mouseup="$mdAutocompleteCtrl.focusInput()"\
role="listbox">\
<li class="md-autocomplete-suggestion" ' + getRepeatType(attr.mdMode) + ' ="item in $mdAutocompleteCtrl.matches"\
ng-class="{ selected: $index === $mdAutocompleteCtrl.index }"\
ng-attr-id="{{\'md-option-\' + $mdAutocompleteCtrl.id + \'-\' + $index}}"\
ng-click="$mdAutocompleteCtrl.select($index)"\
role="option"\
aria-setsize="{{$mdAutocompleteCtrl.matches.length}}"\
aria-posinset="{{$index+1}}"\
aria-selected="{{$index === $mdAutocompleteCtrl.index ? true : false}}" \
md-extra-name="$mdAutocompleteCtrl.itemName">\
' + itemTemplate + '\
</li>' + noItemsTemplate + '\
</ul>\
' + getContainerClosingTags(attr.mdMode) + '\
</md-autocomplete-wrap>';
function getItemTemplate() {
var templateTag = element.find('md-item-template').detach(),
html = templateTag.length ? templateTag.html() : element.html();
if (!templateTag.length) element.empty();
return '<md-autocomplete-parent-scope md-autocomplete-replace>' + html +
'</md-autocomplete-parent-scope>';
}
function getNoItemsTemplate() {
var templateTag = element.find('md-not-found').detach(),
template = templateTag.length ? templateTag.html() : '';
return template
? '<li ng-if="$mdAutocompleteCtrl.notFoundVisible()" class="md-autocomplete-suggestion"\
md-autocomplete-parent-scope>' + template + '</li>'
: '';
}
function getContainer(menuContainerClass, repeatMode) {
// prepend a space if needed
menuContainerClass = menuContainerClass ? ' ' + menuContainerClass : '';
if (isVirtualRepeatDisabled(repeatMode)) {
return '\
<div \
ng-hide="$mdAutocompleteCtrl.hidden"\
class="md-standard-list-container md-autocomplete-suggestions-container md-whiteframe-z1' + menuContainerClass + '"\
ng-class="{ \'md-not-found\': $mdAutocompleteCtrl.notFoundVisible() }"\
ng-mouseenter="$mdAutocompleteCtrl.listEnter()"\
ng-mouseleave="$mdAutocompleteCtrl.listLeave()"\
role="presentation">\
<div class="md-standard-list-scroller" role="presentation">';
}
return '\
<md-virtual-repeat-container\
md-auto-shrink\
md-auto-shrink-min="1"\
ng-hide="$mdAutocompleteCtrl.hidden"\
class="md-virtual-repeat-container md-autocomplete-suggestions-container md-whiteframe-z1' + menuContainerClass + '"\
ng-class="{ \'md-not-found\': $mdAutocompleteCtrl.notFoundVisible() }"\
ng-mouseenter="$mdAutocompleteCtrl.listEnter()"\
ng-mouseleave="$mdAutocompleteCtrl.listLeave()"\
role="presentation">';
}
function getContainerClosingTags(repeatMode) {
return isVirtualRepeatDisabled(repeatMode) ?
' </div>\
</div>\
</div>' : '</md-virtual-repeat-container>';
}
function getRepeatType(repeatMode) {
return isVirtualRepeatDisabled(repeatMode) ?
'ng-repeat' : 'md-virtual-repeat';
}
function isVirtualRepeatDisabled(repeatMode) {
// ensure we have a valid repeat mode
var correctedRepeatMode = getRepeatMode(repeatMode);
return correctedRepeatMode !== REPEAT_VIRTUAL;
}
function getInputElement () {
if (attr.mdFloatingLabel) {
return '\
<md-input-container ng-if="floatingLabel">\
<label>{{floatingLabel}}</label>\
<input type="text"\
' + (tabindex != null ? 'tabindex="' + tabindex + '"' : '') + '\
id="{{inputId || \'fl-input-\' + $mdAutocompleteCtrl.id}}"\
name="{{inputName || \'fl-input-\' + $mdAutocompleteCtrl.id }}"\
ng-class="::inputClass"\
autocomplete="off"\
ng-required="$mdAutocompleteCtrl.isRequired"\
ng-readonly="$mdAutocompleteCtrl.isReadonly"\
ng-minlength="inputMinlength"\
ng-maxlength="inputMaxlength"\
ng-disabled="$mdAutocompleteCtrl.isDisabled"\
ng-model="$mdAutocompleteCtrl.scope.searchText"\
ng-model-options="{ allowInvalid: true }"\
ng-mousedown="$mdAutocompleteCtrl.focusInput()"\
ng-keydown="$mdAutocompleteCtrl.keydown($event)"\
ng-blur="$mdAutocompleteCtrl.blur($event)"\
ng-focus="$mdAutocompleteCtrl.focus($event)"\
aria-label="{{floatingLabel}}"\
ng-attr-aria-autocomplete="{{$mdAutocompleteCtrl.isDisabled ? undefined : \'list\'}}"\
ng-attr-role="{{$mdAutocompleteCtrl.isDisabled ? undefined : \'combobox\'}}"\
aria-haspopup="{{!$mdAutocompleteCtrl.isDisabled}}"\
aria-expanded="{{!$mdAutocompleteCtrl.hidden}}"\
ng-attr-aria-owns="{{$mdAutocompleteCtrl.hidden || $mdAutocompleteCtrl.isDisabled ? undefined : \'ul-\' + $mdAutocompleteCtrl.id}}"\
ng-attr-aria-activedescendant="{{!$mdAutocompleteCtrl.hidden && $mdAutocompleteCtrl.activeOption ? $mdAutocompleteCtrl.activeOption : undefined}}">\
<div md-autocomplete-parent-scope md-autocomplete-replace>' + leftover + '</div>\
</md-input-container>';
} else {
return '\
<input type="text"\
' + (tabindex != null ? 'tabindex="' + tabindex + '"' : '') + '\
id="{{inputId || \'input-\' + $mdAutocompleteCtrl.id}}"\
name="{{inputName || \'input-\' + $mdAutocompleteCtrl.id }}"\
ng-class="::inputClass"\
ng-if="!floatingLabel"\
autocomplete="off"\
ng-required="$mdAutocompleteCtrl.isRequired"\
ng-disabled="$mdAutocompleteCtrl.isDisabled"\
ng-readonly="$mdAutocompleteCtrl.isReadonly"\
ng-minlength="inputMinlength"\
ng-maxlength="inputMaxlength"\
ng-model="$mdAutocompleteCtrl.scope.searchText"\
ng-mousedown="$mdAutocompleteCtrl.focusInput()"\
ng-keydown="$mdAutocompleteCtrl.keydown($event)"\
ng-blur="$mdAutocompleteCtrl.blur($event)"\
ng-focus="$mdAutocompleteCtrl.focus($event)"\
placeholder="{{placeholder}}"\
aria-label="{{placeholder}}"\
ng-attr-aria-autocomplete="{{$mdAutocompleteCtrl.isDisabled ? undefined : \'list\'}}"\
ng-attr-role="{{$mdAutocompleteCtrl.isDisabled ? undefined : \'combobox\'}}"\
aria-haspopup="{{!$mdAutocompleteCtrl.isDisabled}}"\
aria-expanded="{{!$mdAutocompleteCtrl.hidden}}"\
ng-attr-aria-owns="{{$mdAutocompleteCtrl.hidden || $mdAutocompleteCtrl.isDisabled ? undefined : \'ul-\' + $mdAutocompleteCtrl.id}}"\
ng-attr-aria-activedescendant="{{!$mdAutocompleteCtrl.hidden && $mdAutocompleteCtrl.activeOption ? $mdAutocompleteCtrl.activeOption : undefined}}">';
}
}
function getClearButton() {
return '' +
'<button ' +
'type="button" ' +
'aria-label="Clear Input" ' +
'tabindex="0" ' +
'ng-if="clearButton && $mdAutocompleteCtrl.scope.searchText" ' +
'ng-click="$mdAutocompleteCtrl.clear($event)">' +
'<md-icon md-svg-src="' + $$mdSvgRegistry.mdClose + '"></md-icon>' +
'</button>';
}
}
};
}