1 <!DOCTYPE html><html><head><title>Sencha Documentation Project</title><link rel="stylesheet" href="../reset.css" type="text/css"><link rel="stylesheet" href="../prettify.css" type="text/css"><link rel="stylesheet" href="../prettify_sa.css" type="text/css"><script type="text/javascript" src="../prettify.js"></script></head><body onload="prettyPrint()"><pre class="prettyprint"><pre><span id='Ext-form.field.ComboBox-method-constructor'><span id='Ext-form.field.ComboBox'>/**
2 </span></span> * @class Ext.form.field.ComboBox
3 * @extends Ext.form.field.Picker
5 * A combobox control with support for autocomplete, remote loading, and many other features.
7 * A ComboBox is like a combination of a traditional HTML text `&lt;input&gt;` field and a `&lt;select&gt;`
8 * field; the user is able to type freely into the field, and/or pick values from a dropdown selection
9 * list. The user can input any value by default, even if it does not appear in the selection list;
10 * to prevent free-form values and restrict them to items in the list, set {@link #forceSelection} to `true`.
12 * The selection list's options are populated from any {@link Ext.data.Store}, including remote
13 * stores. The data items in the store are mapped to each option's displayed text and backing value via
14 * the {@link #valueField} and {@link #displayField} configurations, respectively.
16 * If your store is not remote, i.e. it depends only on local data and is loaded up front, you should be
17 * sure to set the {@link #queryMode} to `'local'`, as this will improve responsiveness for the user.
19 * {@img Ext.form.ComboBox/Ext.form.ComboBox.png Ext.form.ComboBox component}
23 * // The data store containing the list of states
24 * var states = Ext.create('Ext.data.Store', {
25 * fields: ['abbr', 'name'],
27 * {"abbr":"AL", "name":"Alabama"},
28 * {"abbr":"AK", "name":"Alaska"},
29 * {"abbr":"AZ", "name":"Arizona"}
34 * // Create the combo box, attached to the states data store
35 * Ext.create('Ext.form.ComboBox', {
36 * fieldLabel: 'Choose State',
39 * displayField: 'name',
41 * renderTo: Ext.getBody()
46 * To do something when something in ComboBox is selected, configure the select event:
48 * var cb = Ext.create('Ext.form.ComboBox', {
49 * // all of your config options
52 * 'select': yourFunction
56 * // Alternatively, you can assign events after the object is created:
57 * var cb = new Ext.form.field.ComboBox(yourOptions);
58 * cb.on('select', yourFunction, yourScope);
60 * ## Multiple Selection
62 * ComboBox also allows selection of multiple items from the list; to enable multi-selection set the
63 * {@link #multiSelect} config to `true`.
66 * Create a new ComboBox.
67 * @param {Object} config Configuration options
69 * @docauthor Jason Johnston <jason@sencha.com>
71 Ext.define('Ext.form.field.ComboBox', {
72 extend:'Ext.form.field.Picker',
73 requires: ['Ext.util.DelayedTask', 'Ext.EventObject', 'Ext.view.BoundList', 'Ext.view.BoundListKeyNav', 'Ext.data.StoreManager'],
74 alternateClassName: 'Ext.form.ComboBox',
75 alias: ['widget.combobox', 'widget.combo'],
77 <span id='Ext-form.field.ComboBox-cfg-triggerCls'> /**
78 </span> * @cfg {String} triggerCls
79 * An additional CSS class used to style the trigger button. The trigger will always get the
80 * {@link #triggerBaseCls} by default and <tt>triggerCls</tt> will be <b>appended</b> if specified.
81 * Defaults to 'x-form-arrow-trigger' for ComboBox.
83 triggerCls: Ext.baseCSSPrefix + 'form-arrow-trigger',
85 <span id='Ext-form.field.ComboBox-cfg-store'> /**
86 </span> * @cfg {Ext.data.Store/Array} store The data source to which this combo is bound (defaults to <tt>undefined</tt>).
87 * Acceptable values for this property are:
88 * <div class="mdetail-params"><ul>
89 * <li><b>any {@link Ext.data.Store Store} subclass</b></li>
90 * <li><b>an Array</b> : Arrays will be converted to a {@link Ext.data.Store} internally,
91 * automatically generating {@link Ext.data.Field#name field names} to work with all data components.
92 * <div class="mdetail-params"><ul>
93 * <li><b>1-dimensional array</b> : (e.g., <tt>['Foo','Bar']</tt>)<div class="sub-desc">
94 * A 1-dimensional array will automatically be expanded (each array item will be used for both the combo
95 * {@link #valueField} and {@link #displayField})</div></li>
96 * <li><b>2-dimensional array</b> : (e.g., <tt>[['f','Foo'],['b','Bar']]</tt>)<div class="sub-desc">
97 * For a multi-dimensional array, the value in index 0 of each item will be assumed to be the combo
98 * {@link #valueField}, while the value at index 1 is assumed to be the combo {@link #displayField}.
99 * </div></li></ul></div></li></ul></div>
100 * <p>See also <tt>{@link #queryMode}</tt>.</p>
103 <span id='Ext-form.field.ComboBox-cfg-multiSelect'> /**
104 </span> * @cfg {Boolean} multiSelect
105 * If set to <tt>true</tt>, allows the combo field to hold more than one value at a time, and allows selecting
106 * multiple items from the dropdown list. The combo's text field will show all selected values separated by
107 * the {@link #delimiter}. (Defaults to <tt>false</tt>.)
111 <span id='Ext-form.field.ComboBox-cfg-delimiter'> /**
112 </span> * @cfg {String} delimiter
113 * The character(s) used to separate the {@link #displayField display values} of multiple selected items
114 * when <tt>{@link #multiSelect} = true</tt>. Defaults to <tt>', '</tt>.
118 <span id='Ext-form.field.ComboBox-cfg-displayField'> /**
119 </span> * @cfg {String} displayField The underlying {@link Ext.data.Field#name data field name} to bind to this
120 * ComboBox (defaults to 'text').
121 * <p>See also <tt>{@link #valueField}</tt>.</p>
123 displayField: 'text',
125 <span id='Ext-form.field.ComboBox-cfg-valueField'> /**
126 </span> * @cfg {String} valueField
128 * The underlying {@link Ext.data.Field#name data value name} to bind to this ComboBox (defaults to match
129 * the value of the {@link #displayField} config).
130 * <p><b>Note</b>: use of a <tt>valueField</tt> requires the user to make a selection in order for a value to be
131 * mapped. See also <tt>{@link #displayField}</tt>.</p>
134 <span id='Ext-form.field.ComboBox-cfg-triggerAction'> /**
135 </span> * @cfg {String} triggerAction The action to execute when the trigger is clicked.
136 * <div class="mdetail-params"><ul>
137 * <li><b><tt>'all'</tt></b> : <b>Default</b>
138 * <p class="sub-desc">{@link #doQuery run the query} specified by the <tt>{@link #allQuery}</tt> config option</p></li>
139 * <li><b><tt>'query'</tt></b> :
140 * <p class="sub-desc">{@link #doQuery run the query} using the {@link Ext.form.field.Base#getRawValue raw value}.</p></li>
141 * </ul></div>
142 * <p>See also <code>{@link #queryParam}</code>.</p>
144 triggerAction: 'all',
146 <span id='Ext-form.field.ComboBox-cfg-allQuery'> /**
147 </span> * @cfg {String} allQuery The text query to send to the server to return all records for the list
148 * with no filtering (defaults to '')
152 <span id='Ext-form.field.ComboBox-cfg-queryParam'> /**
153 </span> * @cfg {String} queryParam Name of the query ({@link Ext.data.Store#baseParam baseParam} name for the store)
154 * as it will be passed on the querystring (defaults to <tt>'query'</tt>)
158 <span id='Ext-form.field.ComboBox-cfg-queryMode'> /**
159 </span> * @cfg {String} queryMode
160 * The mode for queries. Acceptable values are:
161 * <div class="mdetail-params"><ul>
162 * <li><b><tt>'remote'</tt></b> : <b>Default</b>
163 * <p class="sub-desc">Automatically loads the <tt>{@link #store}</tt> the <b>first</b> time the trigger
164 * is clicked. If you do not want the store to be automatically loaded the first time the trigger is
165 * clicked, set to <tt>'local'</tt> and manually load the store. To force a requery of the store
166 * <b>every</b> time the trigger is clicked see <tt>{@link #lastQuery}</tt>.</p></li>
167 * <li><b><tt>'local'</tt></b> :
168 * <p class="sub-desc">ComboBox loads local data</p>
169 * <pre><code>
170 var combo = new Ext.form.field.ComboBox({
171 renderTo: document.body,
173 store: new Ext.data.ArrayStore({
176 'myId', // numeric value is the key
179 data: [[1, 'item1'], [2, 'item2']] // data is local
182 displayField: 'displayText',
185 * </code></pre></li>
186 * </ul></div>
192 <span id='Ext-form.field.ComboBox-cfg-pageSize'> /**
193 </span> * @cfg {Number} pageSize If greater than <tt>0</tt>, a {@link Ext.toolbar.Paging} is displayed in the
194 * footer of the dropdown list and the {@link #doQuery filter queries} will execute with page start and
195 * {@link Ext.toolbar.Paging#pageSize limit} parameters. Only applies when <tt>{@link #queryMode} = 'remote'</tt>
196 * (defaults to <tt>0</tt>).
200 <span id='Ext-form.field.ComboBox-cfg-queryDelay'> /**
201 </span> * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and
202 * sending the query to filter the dropdown list (defaults to <tt>500</tt> if <tt>{@link #queryMode} = 'remote'</tt>
203 * or <tt>10</tt> if <tt>{@link #queryMode} = 'local'</tt>)
206 <span id='Ext-form.field.ComboBox-cfg-minChars'> /**
207 </span> * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and
208 * {@link #typeAhead} activate (defaults to <tt>4</tt> if <tt>{@link #queryMode} = 'remote'</tt> or <tt>0</tt> if
209 * <tt>{@link #queryMode} = 'local'</tt>, does not apply if <tt>{@link Ext.form.field.Trigger#editable editable} = false</tt>).
212 <span id='Ext-form.field.ComboBox-cfg-autoSelect'> /**
213 </span> * @cfg {Boolean} autoSelect <tt>true</tt> to select the first result gathered by the data store (defaults
214 * to <tt>true</tt>). A false value would require a manual selection from the dropdown list to set the components value
215 * unless the value of ({@link #typeAhead}) were true.
219 <span id='Ext-form.field.ComboBox-cfg-typeAhead'> /**
220 </span> * @cfg {Boolean} typeAhead <tt>true</tt> to populate and autoselect the remainder of the text being
221 * typed after a configurable delay ({@link #typeAheadDelay}) if it matches a known value (defaults
222 * to <tt>false</tt>)
226 <span id='Ext-form.field.ComboBox-cfg-typeAheadDelay'> /**
227 </span> * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
228 * if <tt>{@link #typeAhead} = true</tt> (defaults to <tt>250</tt>)
232 <span id='Ext-form.field.ComboBox-cfg-selectOnTab'> /**
233 </span> * @cfg {Boolean} selectOnTab
234 * Whether the Tab key should select the currently highlighted item. Defaults to <tt>true</tt>.
238 <span id='Ext-form.field.ComboBox-cfg-forceSelection'> /**
239 </span> * @cfg {Boolean} forceSelection <tt>true</tt> to restrict the selected value to one of the values in the list,
240 * <tt>false</tt> to allow the user to set arbitrary text into the field (defaults to <tt>false</tt>)
242 forceSelection: false,
244 <span id='Ext-form.field.ComboBox-cfg-valueNotFoundText'> /**
245 </span> * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
246 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined). If this
247 * default text is used, it means there is no value set and no validation will occur on this field.
250 <span id='Ext-form.field.ComboBox-property-lastQuery'> /**
251 </span> * The value of the match string used to filter the store. Delete this property to force a requery.
253 * <pre><code>
254 var combo = new Ext.form.field.ComboBox({
258 // delete the previous query in the beforequery event or set
259 // combo.lastQuery = null (this will reload the store the next time it expands)
260 beforequery: function(qe){
261 delete qe.combo.lastQuery;
265 * </code></pre>
266 * To make sure the filter in the store is not cleared the first time the ComboBox trigger is used
267 * configure the combo with <tt>lastQuery=''</tt>. Example use:
268 * <pre><code>
269 var combo = new Ext.form.field.ComboBox({
272 triggerAction: 'all',
275 * </code></pre>
276 * @property lastQuery
280 <span id='Ext-form.field.ComboBox-cfg-defaultListConfig'> /**
281 </span> * @cfg {Object} defaultListConfig
282 * Set of options that will be used as defaults for the user-configured {@link #listConfig} object.
286 loadingText: 'Loading...',
293 <span id='Ext-form.field.ComboBox-cfg-transform'> /**
294 </span> * @cfg {Mixed} transform
295 * The id, DOM node or {@link Ext.core.Element} of an existing HTML <tt>&lt;select&gt;</tt> element to
296 * convert into a ComboBox. The target select's options will be used to build the options in the ComboBox
297 * dropdown; a configured {@link #store} will take precedence over this.
300 <span id='Ext-form.field.ComboBox-cfg-listConfig'> /**
301 </span> * @cfg {Object} listConfig
302 * <p>An optional set of configuration properties that will be passed to the {@link Ext.view.BoundList}'s
303 * constructor. Any configuration that is valid for BoundList can be included. Some of the more useful
304 * ones are:</p>
306 * <li>{@link Ext.view.BoundList#cls} - defaults to empty</li>
307 * <li>{@link Ext.view.BoundList#emptyText} - defaults to empty string</li>
308 * <li>{@link Ext.view.BoundList#getInnerTpl} - defaults to the template defined in BoundList</li>
309 * <li>{@link Ext.view.BoundList#itemSelector} - defaults to the value defined in BoundList</li>
310 * <li>{@link Ext.view.BoundList#loadingText} - defaults to <tt>'Loading...'</tt></li>
311 * <li>{@link Ext.view.BoundList#minWidth} - defaults to <tt>70</tt></li>
312 * <li>{@link Ext.view.BoundList#maxWidth} - defaults to <tt>undefined</tt></li>
313 * <li>{@link Ext.view.BoundList#maxHeight} - defaults to <tt>300</tt></li>
314 * <li>{@link Ext.view.BoundList#resizable} - defaults to <tt>false</tt></li>
315 * <li>{@link Ext.view.BoundList#shadow} - defaults to <tt>'sides'</tt></li>
316 * <li>{@link Ext.view.BoundList#width} - defaults to <tt>undefined</tt> (automatically set to the width
317 * of the ComboBox field if {@link #matchFieldWidth} is true)</li>
324 initComponent: function() {
326 isDefined = Ext.isDefined,
328 transform = me.transform,
329 transformSelect, isLocalMode;
332 if (!store && !transform) {
333 Ext.Error.raise('Either a valid store, or a HTML select to transform, must be configured on the combo.');
335 if (me.typeAhead && me.multiSelect) {
336 Ext.Error.raise('typeAhead and multiSelect are mutually exclusive options -- please remove one of them.');
338 if (me.typeAhead && !me.editable) {
339 Ext.Error.raise('If typeAhead is enabled the combo must be editable: true -- please change one of those settings.');
341 if (me.selectOnFocus && !me.editable) {
342 Ext.Error.raise('If selectOnFocus is enabled the combo must be editable: true -- please change one of those settings.');
347 // TODO need beforeselect?
349 <span id='Ext-form.field.ComboBox-event-beforequery'> /**
350 </span> * @event beforequery
351 * Fires before all queries are processed. Return false to cancel the query or set the queryEvent's
352 * cancel property to true.
353 * @param {Object} queryEvent An object that has these properties:<ul>
354 * <li><code>combo</code> : Ext.form.field.ComboBox <div class="sub-desc">This combo box</div></li>
355 * <li><code>query</code> : String <div class="sub-desc">The query string</div></li>
356 * <li><code>forceAll</code> : Boolean <div class="sub-desc">True to force "all" query</div></li>
357 * <li><code>cancel</code> : Boolean <div class="sub-desc">Set to true to cancel the query</div></li>
364 * Fires when at least one list item is selected.
365 * @param {Ext.form.field.ComboBox} combo This combo box
366 * @param {Array} records The selected records
371 // Build store from 'transform' HTML select element's options
372 if (!store && transform) {
373 transformSelect = Ext.getDom(transform);
374 if (transformSelect) {
375 store = Ext.Array.map(Ext.Array.from(transformSelect.options), function(option) {
376 return [option.value, option.text];
379 me.name = transformSelect.name;
381 if (!('value' in me)) {
382 me.value = transformSelect.value;
387 me.bindStore(store, true);
389 if (store.autoCreated) {
390 me.queryMode = 'local';
391 me.valueField = me.displayField = 'field1';
392 if (!store.expanded) {
393 me.displayField = 'field2';
398 if (!isDefined(me.valueField)) {
399 me.valueField = me.displayField;
402 isLocalMode = me.queryMode === 'local';
403 if (!isDefined(me.queryDelay)) {
404 me.queryDelay = isLocalMode ? 10 : 500;
406 if (!isDefined(me.minChars)) {
407 me.minChars = isLocalMode ? 0 : 4;
410 if (!me.displayTpl) {
411 me.displayTpl = Ext.create('Ext.XTemplate',
412 '<tpl for=".">' +
413 '{[typeof values === "string" ? values : values.' + me.displayField + ']}' +
414 '<tpl if="xindex < xcount">' + me.delimiter + '</tpl>' +
417 } else if (Ext.isString(me.displayTpl)) {
418 me.displayTpl = Ext.create('Ext.XTemplate', me.displayTpl);
423 me.doQueryTask = Ext.create('Ext.util.DelayedTask', me.doRawQuery, me);
425 // store has already been loaded, setValue
426 if (me.store.getCount() > 0) {
427 me.setValue(me.value);
430 // render in place of 'transform' select
431 if (transformSelect) {
432 me.render(transformSelect.parentNode, transformSelect);
433 Ext.removeNode(transformSelect);
438 beforeBlur: function() {
440 me.doQueryTask.cancel();
441 if (me.forceSelection) {
449 assertValue: function() {
451 value = me.getRawValue(),
454 if (me.multiSelect) {
455 // For multiselect, check that the current displayed value matches the current
456 // selection, if it does not then revert to the most recent selection.
457 if (value !== me.getDisplayValue()) {
458 me.setValue(me.lastSelection);
461 // For single-select, match the displayed value to a record and select it,
462 // if it does not match a record then revert to the most recent selection.
463 rec = me.findRecordByDisplay(value);
467 me.setValue(me.lastSelection);
473 onTypeAhead: function() {
475 displayField = me.displayField,
476 record = me.store.findRecord(displayField, me.getRawValue()),
477 boundList = me.getPicker(),
478 newValue, len, selStart;
481 newValue = record.get(displayField);
482 len = newValue.length;
483 selStart = me.getRawValue().length;
485 boundList.highlightItem(boundList.getNode(record));
487 if (selStart !== 0 && selStart !== len) {
488 me.setRawValue(newValue);
489 me.selectText(selStart, newValue.length);
494 // invoked when a different store is bound to this combo
496 resetToDefault: function() {
500 bindStore: function(store, initial) {
504 // this code directly accesses this.picker, bc invoking getPicker
505 // would create it when we may be preping to destroy it
506 if (oldStore && !initial) {
507 if (oldStore !== store && oldStore.autoDestroy) {
513 exception: me.collapse
519 me.picker.bindStore(null);
528 me.store = Ext.data.StoreManager.lookup(store);
532 exception: me.collapse
536 me.picker.bindStore(store);
546 if (me.picker && !me.picker.getSelectionModel().hasSelection()) {
551 <span id='Ext-form.field.ComboBox-method-doRawQuery'> /**
553 * Execute the query with the raw contents within the textfield.
555 doRawQuery: function() {
556 this.doQuery(this.getRawValue());
559 <span id='Ext-form.field.ComboBox-method-doQuery'> /**
560 </span> * Executes a query to filter the dropdown list. Fires the {@link #beforequery} event prior to performing the
561 * query allowing the query action to be canceled if needed.
562 * @param {String} queryString The SQL query to execute
563 * @param {Boolean} forceAll <tt>true</tt> to force the query to execute even if there are currently fewer
564 * characters in the field than the minimum specified by the <tt>{@link #minChars}</tt> config option. It
565 * also clears any filter previously saved in the current store (defaults to <tt>false</tt>)
566 * @return {Boolean} true if the query was permitted to run, false if it was cancelled by a {@link #beforequery} handler.
568 doQuery: function(queryString, forceAll) {
569 queryString = queryString || '';
571 // store in object and pass by reference in 'beforequery'
572 // so that client code can modify values.
581 isLocalMode = me.queryMode === 'local';
583 if (me.fireEvent('beforequery', qe) === false || qe.cancel) {
587 // get back out possibly modified values
588 queryString = qe.query;
589 forceAll = qe.forceAll;
591 // query permitted to run
592 if (forceAll || (queryString.length >= me.minChars)) {
593 // expand before starting query so LoadMask can position itself correctly
596 // make sure they aren't querying the same thing
597 if (!me.queryCaching || me.lastQuery !== queryString) {
598 me.lastQuery = queryString;
599 store.clearFilter(!forceAll);
602 store.filter(me.displayField, queryString);
606 params: me.getParams(queryString)
611 // Clear current selection if it does not match the current value in the field
612 if (me.getRawValue() !== me.getDisplayValue()) {
613 me.ignoreSelection++;
614 me.picker.getSelectionModel().deselectAll();
615 me.ignoreSelection--;
629 getParams: function(queryString) {
631 pageSize = this.pageSize;
632 p[this.queryParam] = queryString;
640 <span id='Ext-form.field.ComboBox-method-doAutoSelect'> /**
642 * If the autoSelect config is true, and the picker is open, highlights the first item.
644 doAutoSelect: function() {
647 lastSelected, itemNode;
648 if (picker && me.autoSelect && me.store.getCount() > 0) {
649 // Highlight the last selected item and scroll it into view
650 lastSelected = picker.getSelectionModel().lastSelected;
651 itemNode = picker.getNode(lastSelected || 0);
653 picker.highlightItem(itemNode);
654 picker.listEl.scrollChildIntoView(itemNode, false);
659 doTypeAhead: function() {
660 if (!this.typeAheadTask) {
661 this.typeAheadTask = Ext.create('Ext.util.DelayedTask', this.onTypeAhead, this);
663 if (this.lastKey != Ext.EventObject.BACKSPACE && this.lastKey != Ext.EventObject.DELETE) {
664 this.typeAheadTask.delay(this.typeAheadDelay);
668 onTriggerClick: function() {
670 if (!me.readOnly && !me.disabled) {
675 if (me.triggerAction === 'all') {
676 me.doQuery(me.allQuery, true);
678 me.doQuery(me.getRawValue());
686 // store the last key and doQuery if relevant
687 onKeyUp: function(e, t) {
691 if (!me.readOnly && !me.disabled && me.editable) {
693 // we put this in a task so that we can cancel it if a user is
694 // in and out before the queryDelay elapses
696 // perform query w/ any normal key or backspace or delete
697 if (!e.isSpecialKey() || key == e.BACKSPACE || key == e.DELETE) {
698 me.doQueryTask.delay(me.queryDelay);
703 initEvents: function() {
707 // setup keyboard handling
708 me.mon(me.inputEl, 'keyup', me.onKeyUp, me);
711 createPicker: function() {
714 menuCls = Ext.baseCSSPrefix + 'menu',
717 mode: me.multiSelect ? 'SIMPLE' : 'SINGLE'
722 cls: me.el.up('.' + menuCls) ? menuCls : '',
724 displayField: me.displayField,
725 focusOnToFront: false,
726 pageSize: me.pageSize
727 }, me.listConfig, me.defaultListConfig);
729 picker = me.picker = Ext.create('Ext.view.BoundList', opts);
732 itemclick: me.onItemClick,
733 refresh: me.onListRefresh,
737 me.mon(picker.getSelectionModel(), {
738 selectionChange: me.onListSelectionChange,
745 onListRefresh: function() {
747 this.syncSelection();
750 onItemClick: function(picker, record){
752 * If we're doing single selection, the selection change events won't fire when
753 * clicking on the selected element. Detect it here.
756 lastSelection = me.lastSelection,
757 valueField = me.valueField,
760 if (!me.multiSelect && lastSelection) {
761 selected = lastSelection[0];
762 if (record.get(valueField) === selected.get(valueField)) {
768 onListSelectionChange: function(list, selectedRecords) {
770 // Only react to selection if it is not called from setValue, and if our list is
771 // expanded (ignores changes to the selection model triggered elsewhere)
772 if (!me.ignoreSelection && me.isExpanded) {
773 if (!me.multiSelect) {
774 Ext.defer(me.collapse, 1, me);
776 me.setValue(selectedRecords, false);
777 if (selectedRecords.length > 0) {
778 me.fireEvent('select', me, selectedRecords);
784 <span id='Ext-form.field.ComboBox-method-onExpand'> /**
786 * Enables the key nav for the BoundList when it is expanded.
788 onExpand: function() {
790 keyNav = me.listKeyNav,
791 selectOnTab = me.selectOnTab,
792 picker = me.getPicker();
794 // Handle BoundList navigation from the input field. Insert a tab listener specially to enable selectOnTab.
798 keyNav = me.listKeyNav = Ext.create('Ext.view.BoundListKeyNav', this.inputEl, {
803 this.selectHighlighted(e);
806 // Tab key event is allowed to propagate to field
812 // While list is expanded, stop tab monitoring from Ext.form.field.Trigger so it doesn't short-circuit selectOnTab
814 me.ignoreMonitorTab = true;
817 Ext.defer(keyNav.enable, 1, keyNav); //wait a bit so it doesn't react to the down arrow opening the picker
821 <span id='Ext-form.field.ComboBox-method-onCollapse'> /**
823 * Disables the key nav for the BoundList when it is collapsed.
825 onCollapse: function() {
827 keyNav = me.listKeyNav;
830 me.ignoreMonitorTab = false;
834 <span id='Ext-form.field.ComboBox-method-select'> /**
835 </span> * Selects an item by a {@link Ext.data.Model Model}, or by a key value.
838 select: function(r) {
839 this.setValue(r, true);
842 <span id='Ext-form.field.ComboBox-method-findRecord'> /**
843 </span> * Find the record by searching for a specific field/value combination
844 * Returns an Ext.data.Record or false
847 findRecord: function(field, value) {
849 idx = ds.findExact(field, value);
850 return idx !== -1 ? ds.getAt(idx) : false;
852 findRecordByValue: function(value) {
853 return this.findRecord(this.valueField, value);
855 findRecordByDisplay: function(value) {
856 return this.findRecord(this.displayField, value);
859 <span id='Ext-form.field.ComboBox-method-setValue'> /**
860 </span> * Sets the specified value(s) into the field. For each value, if a record is found in the {@link #store} that
861 * matches based on the {@link #valueField}, then that record's {@link #displayField} will be displayed in the
862 * field. If no match is found, and the {@link #valueNotFoundText} config option is defined, then that will be
863 * displayed as the default field text. Otherwise a blank value will be shown, although the value will still be set.
864 * @param {String|Array} value The value(s) to be set. Can be either a single String or {@link Ext.data.Model},
865 * or an Array of Strings or Models.
866 * @return {Ext.form.field.Field} this
868 setValue: function(value, doSelect) {
870 valueNotFoundText = me.valueNotFoundText,
871 inputEl = me.inputEl,
877 if (me.store.loading) {
878 // Called while the Store is loading. Ensure it is processed by the onLoad method.
883 // This method processes multi-values, so ensure value is an array.
884 value = Ext.Array.from(value);
886 // Loop through values
887 for (i = 0, len = value.length; i < len; i++) {
889 if (!record || !record.isModel) {
890 record = me.findRecordByValue(record);
892 // record found, select it.
895 displayTplData.push(record.data);
896 processedValue.push(record.get(me.valueField));
898 // record was not found, this could happen because
899 // store is not loaded or they set a value not in the store
901 // if valueNotFoundText is defined, display it, otherwise display nothing for this value
902 if (Ext.isDefined(valueNotFoundText)) {
903 displayTplData.push(valueNotFoundText);
905 processedValue.push(value[i]);
909 // Set the value of this field. If we are multiselecting, then that is an array.
910 me.value = me.multiSelect ? processedValue : processedValue[0];
911 if (!Ext.isDefined(me.value)) {
914 me.displayTplData = displayTplData; //store for getDisplayValue method
915 me.lastSelection = me.valueModels = models;
917 if (inputEl && me.emptyText && !Ext.isEmpty(value)) {
918 inputEl.removeCls(me.emptyCls);
921 // Calculate raw value from the collection of Model data
922 me.setRawValue(me.getDisplayValue());
925 if (doSelect !== false) {
933 <span id='Ext-form.field.ComboBox-method-getDisplayValue'> /**
934 </span> * @private Generate the string value to be displayed in the text field for the currently stored value
936 getDisplayValue: function() {
937 return this.displayTpl.apply(this.displayTplData);
940 getValue: function() {
941 // If the user has not changed the raw field value since a value was selected from the list,
942 // then return the structured value from the selection. If the raw field value is different
943 // than what would be displayed due to selection, return that raw value.
946 rawValue = me.getRawValue(), //current value of text field
947 value = me.value; //stored value from last selection or setValue() call
949 if (me.getDisplayValue() !== rawValue) {
951 me.value = me.displayTplData = me.valueModels = null;
953 me.ignoreSelection++;
954 picker.getSelectionModel().deselectAll();
955 me.ignoreSelection--;
962 getSubmitValue: function() {
963 return this.getValue();
966 isEqual: function(v1, v2) {
967 var fromArray = Ext.Array.from,
974 if (len !== v2.length) {
978 for(i = 0; i < len; i++) {
979 if (v2[i] !== v1[i]) {
987 <span id='Ext-form.field.ComboBox-method-clearValue'> /**
988 </span> * Clears any value currently set in the ComboBox.
990 clearValue: function() {
994 <span id='Ext-form.field.ComboBox-method-syncSelection'> /**
995 </span> * @private Synchronizes the selection in the picker to match the current value of the combobox.
997 syncSelection: function() {
999 ExtArray = Ext.Array,
1001 selection, selModel;
1003 // From the value, find the Models that are in the store's current data
1005 ExtArray.forEach(me.valueModels || [], function(value) {
1006 if (value && value.isModel && me.store.indexOf(value) >= 0) {
1007 selection.push(value);
1011 // Update the selection to match
1012 me.ignoreSelection++;
1013 selModel = picker.getSelectionModel();
1014 selModel.deselectAll();
1015 if (selection.length) {
1016 selModel.select(selection);
1018 me.ignoreSelection--;
1022 </pre></pre></body></html>