ComboBox2.html 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  5. <title>The source code</title>
  6. <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  7. <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  8. <style type="text/css">
  9. .highlight { display: block; background-color: #ddd; }
  10. </style>
  11. <script type="text/javascript">
  12. function highlight() {
  13. document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
  14. }
  15. </script>
  16. </head>
  17. <body onload="prettyPrint(); highlight();">
  18. <pre class="prettyprint lang-js"><span id='Ext-form-field-ComboBox'>/**
  19. </span> * @docauthor Jason Johnston &lt;jason@sencha.com&gt;
  20. *
  21. * A combobox control with support for autocomplete, remote loading, and many other features.
  22. *
  23. * A ComboBox is like a combination of a traditional HTML text `&lt;input&gt;` field and a `&lt;select&gt;`
  24. * field; the user is able to type freely into the field, and/or pick values from a dropdown selection
  25. * list. The user can input any value by default, even if it does not appear in the selection list;
  26. * to prevent free-form values and restrict them to items in the list, set {@link #forceSelection} to `true`.
  27. *
  28. * The selection list's options are populated from any {@link Ext.data.Store}, including remote
  29. * stores. The data items in the store are mapped to each option's displayed text and backing value via
  30. * the {@link #valueField} and {@link #displayField} configurations, respectively.
  31. *
  32. * If your store is not remote, i.e. it depends only on local data and is loaded up front, you should be
  33. * sure to set the {@link #queryMode} to `'local'`, as this will improve responsiveness for the user.
  34. *
  35. * # Example usage:
  36. *
  37. * @example
  38. * // The data store containing the list of states
  39. * var states = Ext.create('Ext.data.Store', {
  40. * fields: ['abbr', 'name'],
  41. * data : [
  42. * {&quot;abbr&quot;:&quot;AL&quot;, &quot;name&quot;:&quot;Alabama&quot;},
  43. * {&quot;abbr&quot;:&quot;AK&quot;, &quot;name&quot;:&quot;Alaska&quot;},
  44. * {&quot;abbr&quot;:&quot;AZ&quot;, &quot;name&quot;:&quot;Arizona&quot;}
  45. * //...
  46. * ]
  47. * });
  48. *
  49. * // Create the combo box, attached to the states data store
  50. * Ext.create('Ext.form.ComboBox', {
  51. * fieldLabel: 'Choose State',
  52. * store: states,
  53. * queryMode: 'local',
  54. * displayField: 'name',
  55. * valueField: 'abbr',
  56. * renderTo: Ext.getBody()
  57. * });
  58. *
  59. * # Events
  60. *
  61. * To do something when something in ComboBox is selected, configure the select event:
  62. *
  63. * var cb = Ext.create('Ext.form.ComboBox', {
  64. * // all of your config options
  65. * listeners:{
  66. * scope: yourScope,
  67. * 'select': yourFunction
  68. * }
  69. * });
  70. *
  71. * // Alternatively, you can assign events after the object is created:
  72. * var cb = new Ext.form.field.ComboBox(yourOptions);
  73. * cb.on('select', yourFunction, yourScope);
  74. *
  75. * # Multiple Selection
  76. *
  77. * ComboBox also allows selection of multiple items from the list; to enable multi-selection set the
  78. * {@link #multiSelect} config to `true`.
  79. *
  80. * # Filtered Stores
  81. *
  82. * If you have a local store that is already filtered, you can use the {@link #lastQuery} config option
  83. * to prevent the store from having the filter being cleared on first expand.
  84. *
  85. * ## Customized combobox
  86. *
  87. * Both the text shown in dropdown menu and text field can be easily customized:
  88. *
  89. * @example
  90. * var states = Ext.create('Ext.data.Store', {
  91. * fields: ['abbr', 'name'],
  92. * data : [
  93. * {&quot;abbr&quot;:&quot;AL&quot;, &quot;name&quot;:&quot;Alabama&quot;},
  94. * {&quot;abbr&quot;:&quot;AK&quot;, &quot;name&quot;:&quot;Alaska&quot;},
  95. * {&quot;abbr&quot;:&quot;AZ&quot;, &quot;name&quot;:&quot;Arizona&quot;}
  96. * ]
  97. * });
  98. *
  99. * Ext.create('Ext.form.ComboBox', {
  100. * fieldLabel: 'Choose State',
  101. * store: states,
  102. * queryMode: 'local',
  103. * valueField: 'abbr',
  104. * renderTo: Ext.getBody(),
  105. * // Template for the dropdown menu.
  106. * // Note the use of &quot;x-boundlist-item&quot; class,
  107. * // this is required to make the items selectable.
  108. * tpl: Ext.create('Ext.XTemplate',
  109. * '&lt;tpl for=&quot;.&quot;&gt;',
  110. * '&lt;div class=&quot;x-boundlist-item&quot;&gt;{abbr} - {name}&lt;/div&gt;',
  111. * '&lt;/tpl&gt;'
  112. * ),
  113. * // template for the content inside text field
  114. * displayTpl: Ext.create('Ext.XTemplate',
  115. * '&lt;tpl for=&quot;.&quot;&gt;',
  116. * '{abbr} - {name}',
  117. * '&lt;/tpl&gt;'
  118. * )
  119. * });
  120. *
  121. * See also the {@link #listConfig} option for additional configuration of the dropdown.
  122. *
  123. */
  124. Ext.define('Ext.form.field.ComboBox', {
  125. extend:'Ext.form.field.Picker',
  126. requires: ['Ext.util.DelayedTask', 'Ext.EventObject', 'Ext.view.BoundList', 'Ext.view.BoundListKeyNav', 'Ext.data.StoreManager', 'Ext.layout.component.field.ComboBox'],
  127. alternateClassName: 'Ext.form.ComboBox',
  128. alias: ['widget.combobox', 'widget.combo'],
  129. mixins: {
  130. bindable: 'Ext.util.Bindable'
  131. },
  132. componentLayout: 'combobox',
  133. <span id='Ext-form-field-ComboBox-cfg-triggerCls'> /**
  134. </span> * @cfg {String} [triggerCls='x-form-arrow-trigger']
  135. * An additional CSS class used to style the trigger button. The trigger will always get the {@link #triggerBaseCls}
  136. * by default and `triggerCls` will be **appended** if specified.
  137. */
  138. triggerCls: Ext.baseCSSPrefix + 'form-arrow-trigger',
  139. <span id='Ext-form-field-ComboBox-cfg-hiddenName'> /**
  140. </span> * @cfg {String} [hiddenName=&quot;&quot;]
  141. * The name of an underlying hidden field which will be synchronized with the underlying value of the combo.
  142. * This option is useful if the combo is part of a form element doing a regular form post. The hidden field
  143. * will not be created unless a hiddenName is specified.
  144. */
  145. hiddenName: '',
  146. <span id='Ext-form-field-ComboBox-property-hiddenDataEl'> /**
  147. </span> * @property {Ext.Element} hiddenDataEl
  148. * @private
  149. */
  150. <span id='Ext-form-field-ComboBox-cfg-hiddenDataCls'> /**
  151. </span> * @private
  152. * @cfg {String}
  153. * CSS class used to find the {@link #hiddenDataEl}
  154. */
  155. hiddenDataCls: Ext.baseCSSPrefix + 'hide-display ' + Ext.baseCSSPrefix + 'form-data-hidden',
  156. <span id='Ext-form-field-ComboBox-cfg-fieldSubTpl'> /**
  157. </span> * @cfg
  158. * @inheritdoc
  159. */
  160. fieldSubTpl: [
  161. '&lt;div class=&quot;{hiddenDataCls}&quot; role=&quot;presentation&quot;&gt;&lt;/div&gt;',
  162. '&lt;input id=&quot;{id}&quot; type=&quot;{type}&quot; {inputAttrTpl} class=&quot;{fieldCls} {typeCls}&quot; autocomplete=&quot;off&quot;',
  163. '&lt;tpl if=&quot;value&quot;&gt; value=&quot;{[Ext.util.Format.htmlEncode(values.value)]}&quot;&lt;/tpl&gt;',
  164. '&lt;tpl if=&quot;name&quot;&gt; name=&quot;{name}&quot;&lt;/tpl&gt;',
  165. '&lt;tpl if=&quot;placeholder&quot;&gt; placeholder=&quot;{placeholder}&quot;&lt;/tpl&gt;',
  166. '&lt;tpl if=&quot;size&quot;&gt; size=&quot;{size}&quot;&lt;/tpl&gt;',
  167. '&lt;tpl if=&quot;maxLength !== undefined&quot;&gt; maxlength=&quot;{maxLength}&quot;&lt;/tpl&gt;',
  168. '&lt;tpl if=&quot;readOnly&quot;&gt; readonly=&quot;readonly&quot;&lt;/tpl&gt;',
  169. '&lt;tpl if=&quot;disabled&quot;&gt; disabled=&quot;disabled&quot;&lt;/tpl&gt;',
  170. '&lt;tpl if=&quot;tabIdx&quot;&gt; tabIndex=&quot;{tabIdx}&quot;&lt;/tpl&gt;',
  171. '&lt;tpl if=&quot;fieldStyle&quot;&gt; style=&quot;{fieldStyle}&quot;&lt;/tpl&gt;',
  172. '/&gt;',
  173. {
  174. compiled: true,
  175. disableFormats: true
  176. }
  177. ],
  178. getSubTplData: function(){
  179. var me = this;
  180. Ext.applyIf(me.subTplData, {
  181. hiddenDataCls: me.hiddenDataCls
  182. });
  183. return me.callParent(arguments);
  184. },
  185. afterRender: function(){
  186. var me = this;
  187. me.callParent(arguments);
  188. me.setHiddenValue(me.value);
  189. },
  190. <span id='Ext-form-field-ComboBox-cfg-store'> /**
  191. </span> * @cfg {Ext.data.Store/Array} store
  192. * The data source to which this combo is bound. Acceptable values for this property are:
  193. *
  194. * - **any {@link Ext.data.Store Store} subclass**
  195. * - **an Array** : Arrays will be converted to a {@link Ext.data.Store} internally, automatically generating
  196. * {@link Ext.data.Field#name field names} to work with all data components.
  197. *
  198. * - **1-dimensional array** : (e.g., `['Foo','Bar']`)
  199. *
  200. * A 1-dimensional array will automatically be expanded (each array item will be used for both the combo
  201. * {@link #valueField} and {@link #displayField})
  202. *
  203. * - **2-dimensional array** : (e.g., `[['f','Foo'],['b','Bar']]`)
  204. *
  205. * For a multi-dimensional array, the value in index 0 of each item will be assumed to be the combo
  206. * {@link #valueField}, while the value at index 1 is assumed to be the combo {@link #displayField}.
  207. *
  208. * See also {@link #queryMode}.
  209. */
  210. <span id='Ext-form-field-ComboBox-cfg-multiSelect'> /**
  211. </span> * @cfg {Boolean} multiSelect
  212. * If set to `true`, allows the combo field to hold more than one value at a time, and allows selecting multiple
  213. * items from the dropdown list. The combo's text field will show all selected values separated by the
  214. * {@link #delimiter}.
  215. */
  216. multiSelect: false,
  217. //&lt;locale&gt;
  218. <span id='Ext-form-field-ComboBox-cfg-delimiter'> /**
  219. </span> * @cfg {String} delimiter
  220. * The character(s) used to separate the {@link #displayField display values} of multiple selected items when
  221. * `{@link #multiSelect} = true`.
  222. */
  223. delimiter: ', ',
  224. //&lt;/locale&gt;
  225. <span id='Ext-form-field-ComboBox-cfg-displayField'> /**
  226. </span> * @cfg {String} displayField
  227. * The underlying {@link Ext.data.Field#name data field name} to bind to this ComboBox.
  228. *
  229. * See also `{@link #valueField}`.
  230. */
  231. displayField: 'text',
  232. <span id='Ext-form-field-ComboBox-cfg-valueField'> /**
  233. </span> * @cfg {String} valueField (required)
  234. * The underlying {@link Ext.data.Field#name data value name} to bind to this ComboBox.
  235. *
  236. * **Note**: use of a `valueField` requires the user to make a selection in order for a value to be mapped. See also
  237. * `{@link #displayField}`.
  238. *
  239. * Defaults to match the value of the {@link #displayField} config.
  240. */
  241. <span id='Ext-form-field-ComboBox-cfg-triggerAction'> /**
  242. </span> * @cfg {String} triggerAction
  243. * The action to execute when the trigger is clicked.
  244. *
  245. * - **`'all'`** :
  246. *
  247. * {@link #doQuery run the query} specified by the `{@link #allQuery}` config option
  248. *
  249. * - **`'query'`** :
  250. *
  251. * {@link #doQuery run the query} using the {@link Ext.form.field.Base#getRawValue raw value}.
  252. *
  253. * See also `{@link #queryParam}`.
  254. */
  255. triggerAction: 'all',
  256. <span id='Ext-form-field-ComboBox-cfg-allQuery'> /**
  257. </span> * @cfg {String} allQuery
  258. * The text query to send to the server to return all records for the list with no filtering
  259. */
  260. allQuery: '',
  261. <span id='Ext-form-field-ComboBox-cfg-queryParam'> /**
  262. </span> * @cfg {String} queryParam
  263. * Name of the parameter used by the Store to pass the typed string when the ComboBox is configured with
  264. * `{@link #queryMode}: 'remote'`. If explicitly set to a falsy value it will not be sent.
  265. */
  266. queryParam: 'query',
  267. <span id='Ext-form-field-ComboBox-cfg-queryMode'> /**
  268. </span> * @cfg {String} queryMode
  269. * The mode in which the ComboBox uses the configured Store. Acceptable values are:
  270. *
  271. * - **`'remote'`** :
  272. *
  273. * In `queryMode: 'remote'`, the ComboBox loads its Store dynamically based upon user interaction.
  274. *
  275. * This is typically used for &quot;autocomplete&quot; type inputs, and after the user finishes typing, the Store is {@link
  276. * Ext.data.Store#method-load load}ed.
  277. *
  278. * A parameter containing the typed string is sent in the load request. The default parameter name for the input
  279. * string is `query`, but this can be configured using the {@link #queryParam} config.
  280. *
  281. * In `queryMode: 'remote'`, the Store may be configured with `{@link Ext.data.Store#remoteFilter remoteFilter}:
  282. * true`, and further filters may be _programatically_ added to the Store which are then passed with every load
  283. * request which allows the server to further refine the returned dataset.
  284. *
  285. * Typically, in an autocomplete situation, {@link #hideTrigger} is configured `true` because it has no meaning for
  286. * autocomplete.
  287. *
  288. * - **`'local'`** :
  289. *
  290. * ComboBox loads local data
  291. *
  292. * var combo = new Ext.form.field.ComboBox({
  293. * renderTo: document.body,
  294. * queryMode: 'local',
  295. * store: new Ext.data.ArrayStore({
  296. * id: 0,
  297. * fields: [
  298. * 'myId', // numeric value is the key
  299. * 'displayText'
  300. * ],
  301. * data: [[1, 'item1'], [2, 'item2']] // data is local
  302. * }),
  303. * valueField: 'myId',
  304. * displayField: 'displayText',
  305. * triggerAction: 'all'
  306. * });
  307. */
  308. queryMode: 'remote',
  309. <span id='Ext-form-field-ComboBox-cfg-queryCaching'> /**
  310. </span> * @cfg {Boolean} [queryCaching=true]
  311. * When true, this prevents the combo from re-querying (either locally or remotely) when the current query
  312. * is the same as the previous query.
  313. */
  314. queryCaching: true,
  315. <span id='Ext-form-field-ComboBox-cfg-pageSize'> /**
  316. </span> * @cfg {Number} pageSize
  317. * If greater than `0`, a {@link Ext.toolbar.Paging} is displayed in the footer of the dropdown list and the
  318. * {@link #doQuery filter queries} will execute with page start and {@link Ext.view.BoundList#pageSize limit}
  319. * parameters. Only applies when `{@link #queryMode} = 'remote'`.
  320. */
  321. pageSize: 0,
  322. <span id='Ext-form-field-ComboBox-cfg-queryDelay'> /**
  323. </span> * @cfg {Number} queryDelay
  324. * The length of time in milliseconds to delay between the start of typing and sending the query to filter the
  325. * dropdown list.
  326. *
  327. * Defaults to `500` if `{@link #queryMode} = 'remote'` or `10` if `{@link #queryMode} = 'local'`
  328. */
  329. <span id='Ext-form-field-ComboBox-cfg-minChars'> /**
  330. </span> * @cfg {Number} minChars
  331. * The minimum number of characters the user must type before autocomplete and {@link #typeAhead} activate.
  332. *
  333. * Defaults to `4` if `{@link #queryMode} = 'remote'` or `0` if `{@link #queryMode} = 'local'`,
  334. * does not apply if `{@link Ext.form.field.Trigger#editable editable} = false`.
  335. */
  336. <span id='Ext-form-field-ComboBox-cfg-autoSelect'> /**
  337. </span> * @cfg {Boolean} autoSelect
  338. * `true` to automatically highlight the first result gathered by the data store in the dropdown list when it is
  339. * opened. A false value would cause nothing in the list to be highlighted automatically, so
  340. * the user would have to manually highlight an item before pressing the enter or {@link #selectOnTab tab} key to
  341. * select it (unless the value of ({@link #typeAhead}) were true), or use the mouse to select a value.
  342. */
  343. autoSelect: true,
  344. <span id='Ext-form-field-ComboBox-cfg-typeAhead'> /**
  345. </span> * @cfg {Boolean} typeAhead
  346. * `true` to populate and autoselect the remainder of the text being typed after a configurable delay
  347. * ({@link #typeAheadDelay}) if it matches a known value.
  348. */
  349. typeAhead: false,
  350. <span id='Ext-form-field-ComboBox-cfg-typeAheadDelay'> /**
  351. </span> * @cfg {Number} typeAheadDelay
  352. * The length of time in milliseconds to wait until the typeahead text is displayed if `{@link #typeAhead} = true`
  353. */
  354. typeAheadDelay: 250,
  355. <span id='Ext-form-field-ComboBox-cfg-selectOnTab'> /**
  356. </span> * @cfg {Boolean} selectOnTab
  357. * Whether the Tab key should select the currently highlighted item.
  358. */
  359. selectOnTab: true,
  360. <span id='Ext-form-field-ComboBox-cfg-forceSelection'> /**
  361. </span> * @cfg {Boolean} forceSelection
  362. * `true` to restrict the selected value to one of the values in the list, `false` to allow the user to set
  363. * arbitrary text into the field.
  364. */
  365. forceSelection: false,
  366. <span id='Ext-form-field-ComboBox-cfg-growToLongestValue'> /**
  367. </span> * @cfg {Boolean} growToLongestValue
  368. * `false` to not allow the component to resize itself when its data changes
  369. * (and its {@link #grow} property is `true`)
  370. */
  371. growToLongestValue: true,
  372. <span id='Ext-form-field-ComboBox-cfg-valueNotFoundText'> /**
  373. </span> * @cfg {String} valueNotFoundText
  374. * When using a name/value combo, if the value passed to setValue is not found in the store, valueNotFoundText will
  375. * be displayed as the field text if defined. If this default text is used, it means there
  376. * is no value set and no validation will occur on this field.
  377. */
  378. <span id='Ext-form-field-ComboBox-property-lastQuery'> /**
  379. </span> * @property {String} lastQuery
  380. * The value of the match string used to filter the store. Delete this property to force a requery. Example use:
  381. *
  382. * var combo = new Ext.form.field.ComboBox({
  383. * ...
  384. * queryMode: 'remote',
  385. * listeners: {
  386. * // delete the previous query in the beforequery event or set
  387. * // combo.lastQuery = null (this will reload the store the next time it expands)
  388. * beforequery: function(qe){
  389. * delete qe.combo.lastQuery;
  390. * }
  391. * }
  392. * });
  393. *
  394. * To make sure the filter in the store is not cleared the first time the ComboBox trigger is used configure the
  395. * combo with `lastQuery=''`. Example use:
  396. *
  397. * var combo = new Ext.form.field.ComboBox({
  398. * ...
  399. * queryMode: 'local',
  400. * triggerAction: 'all',
  401. * lastQuery: ''
  402. * });
  403. */
  404. <span id='Ext-form-field-ComboBox-cfg-defaultListConfig'> /**
  405. </span> * @cfg {Object} defaultListConfig
  406. * Set of options that will be used as defaults for the user-configured {@link #listConfig} object.
  407. */
  408. defaultListConfig: {
  409. loadingHeight: 70,
  410. minWidth: 70,
  411. maxHeight: 300,
  412. shadow: 'sides'
  413. },
  414. <span id='Ext-form-field-ComboBox-cfg-transform'> /**
  415. </span> * @cfg {String/HTMLElement/Ext.Element} transform
  416. * The id, DOM node or {@link Ext.Element} of an existing HTML `&lt;select&gt;` element to convert into a ComboBox. The
  417. * target select's options will be used to build the options in the ComboBox dropdown; a configured {@link #store}
  418. * will take precedence over this.
  419. */
  420. <span id='Ext-form-field-ComboBox-cfg-listConfig'> /**
  421. </span> * @cfg {Object} listConfig
  422. * An optional set of configuration properties that will be passed to the {@link Ext.view.BoundList}'s constructor.
  423. * Any configuration that is valid for BoundList can be included. Some of the more useful ones are:
  424. *
  425. * - {@link Ext.view.BoundList#cls cls} - defaults to empty
  426. * - {@link Ext.view.BoundList#emptyText emptyText} - defaults to empty string
  427. * - {@link Ext.view.BoundList#itemSelector itemSelector} - defaults to the value defined in BoundList
  428. * - {@link Ext.view.BoundList#loadingText loadingText} - defaults to `'Loading...'`
  429. * - {@link Ext.view.BoundList#minWidth minWidth} - defaults to `70`
  430. * - {@link Ext.view.BoundList#maxWidth maxWidth} - defaults to `undefined`
  431. * - {@link Ext.view.BoundList#maxHeight maxHeight} - defaults to `300`
  432. * - {@link Ext.view.BoundList#resizable resizable} - defaults to `false`
  433. * - {@link Ext.view.BoundList#shadow shadow} - defaults to `'sides'`
  434. * - {@link Ext.view.BoundList#width width} - defaults to `undefined` (automatically set to the width of the ComboBox
  435. * field if {@link #matchFieldWidth} is true)
  436. */
  437. //private
  438. ignoreSelection: 0,
  439. //private, tells the layout to recalculate its startingWidth when a record is removed from its bound store
  440. removingRecords: null,
  441. //private helper
  442. resizeComboToGrow: function () {
  443. var me = this;
  444. return me.grow &amp;&amp; me.growToLongestValue;
  445. },
  446. initComponent: function() {
  447. var me = this,
  448. isDefined = Ext.isDefined,
  449. store = me.store,
  450. transform = me.transform,
  451. transformSelect, isLocalMode;
  452. Ext.applyIf(me.renderSelectors, {
  453. hiddenDataEl: '.' + me.hiddenDataCls.split(' ').join('.')
  454. });
  455. //&lt;debug&gt;
  456. if (me.typeAhead &amp;&amp; me.multiSelect) {
  457. Ext.Error.raise('typeAhead and multiSelect are mutually exclusive options -- please remove one of them.');
  458. }
  459. if (me.typeAhead &amp;&amp; !me.editable) {
  460. Ext.Error.raise('If typeAhead is enabled the combo must be editable: true -- please change one of those settings.');
  461. }
  462. if (me.selectOnFocus &amp;&amp; !me.editable) {
  463. Ext.Error.raise('If selectOnFocus is enabled the combo must be editable: true -- please change one of those settings.');
  464. }
  465. //&lt;/debug&gt;
  466. this.addEvents(
  467. <span id='Ext-form-field-ComboBox-event-beforequery'> /**
  468. </span> * @event beforequery
  469. * Fires before all queries are processed. Return false to cancel the query or set the queryEvent's cancel
  470. * property to true.
  471. *
  472. * @param {Object} queryEvent An object that has these properties:
  473. *
  474. * - `combo` : Ext.form.field.ComboBox
  475. *
  476. * This combo box
  477. *
  478. * - `query` : String
  479. *
  480. * The query string
  481. *
  482. * - `forceAll` : Boolean
  483. *
  484. * True to force &quot;all&quot; query
  485. *
  486. * - `cancel` : Boolean
  487. *
  488. * Set to true to cancel the query
  489. */
  490. 'beforequery',
  491. <span id='Ext-form-field-ComboBox-event-select'> /**
  492. </span> * @event select
  493. * Fires when at least one list item is selected.
  494. * @param {Ext.form.field.ComboBox} combo This combo box
  495. * @param {Array} records The selected records
  496. */
  497. 'select',
  498. <span id='Ext-form-field-ComboBox-event-beforeselect'> /**
  499. </span> * @event beforeselect
  500. * Fires before the selected item is added to the collection
  501. * @param {Ext.form.field.ComboBox} combo This combo box
  502. * @param {Ext.data.Record} record The selected record
  503. * @param {Number} index The index of the selected record
  504. */
  505. 'beforeselect',
  506. <span id='Ext-form-field-ComboBox-event-beforedeselect'> /**
  507. </span> * @event beforedeselect
  508. * Fires before the deselected item is removed from the collection
  509. * @param {Ext.form.field.ComboBox} combo This combo box
  510. * @param {Ext.data.Record} record The deselected record
  511. * @param {Number} index The index of the deselected record
  512. */
  513. 'beforedeselect'
  514. );
  515. // Build store from 'transform' HTML select element's options
  516. if (transform) {
  517. transformSelect = Ext.getDom(transform);
  518. if (transformSelect) {
  519. if (!me.store) {
  520. store = Ext.Array.map(Ext.Array.from(transformSelect.options), function(option){
  521. return [option.value, option.text];
  522. });
  523. }
  524. if (!me.name) {
  525. me.name = transformSelect.name;
  526. }
  527. if (!('value' in me)) {
  528. me.value = transformSelect.value;
  529. }
  530. }
  531. }
  532. me.bindStore(store || 'ext-empty-store', true);
  533. store = me.store;
  534. if (store.autoCreated) {
  535. me.queryMode = 'local';
  536. me.valueField = me.displayField = 'field1';
  537. if (!store.expanded) {
  538. me.displayField = 'field2';
  539. }
  540. }
  541. if (!isDefined(me.valueField)) {
  542. me.valueField = me.displayField;
  543. }
  544. isLocalMode = me.queryMode === 'local';
  545. if (!isDefined(me.queryDelay)) {
  546. me.queryDelay = isLocalMode ? 10 : 500;
  547. }
  548. if (!isDefined(me.minChars)) {
  549. me.minChars = isLocalMode ? 0 : 4;
  550. }
  551. if (!me.displayTpl) {
  552. me.displayTpl = new Ext.XTemplate(
  553. '&lt;tpl for=&quot;.&quot;&gt;' +
  554. '{[typeof values === &quot;string&quot; ? values : values[&quot;' + me.displayField + '&quot;]]}' +
  555. '&lt;tpl if=&quot;xindex &lt; xcount&quot;&gt;' + me.delimiter + '&lt;/tpl&gt;' +
  556. '&lt;/tpl&gt;'
  557. );
  558. } else if (Ext.isString(me.displayTpl)) {
  559. me.displayTpl = new Ext.XTemplate(me.displayTpl);
  560. }
  561. me.callParent();
  562. me.doQueryTask = new Ext.util.DelayedTask(me.doRawQuery, me);
  563. // store has already been loaded, setValue
  564. if (me.store.getCount() &gt; 0) {
  565. me.setValue(me.value);
  566. }
  567. // render in place of 'transform' select
  568. if (transformSelect) {
  569. me.render(transformSelect.parentNode, transformSelect);
  570. Ext.removeNode(transformSelect);
  571. delete me.renderTo;
  572. }
  573. },
  574. <span id='Ext-form-field-ComboBox-method-getStore'> /**
  575. </span> * Returns the store associated with this ComboBox.
  576. * @return {Ext.data.Store} The store
  577. */
  578. getStore : function(){
  579. return this.store;
  580. },
  581. beforeBlur: function() {
  582. this.doQueryTask.cancel();
  583. this.assertValue();
  584. },
  585. // private
  586. assertValue: function() {
  587. var me = this,
  588. value = me.getRawValue(),
  589. rec;
  590. if (me.forceSelection) {
  591. if (me.multiSelect) {
  592. // For multiselect, check that the current displayed value matches the current
  593. // selection, if it does not then revert to the most recent selection.
  594. if (value !== me.getDisplayValue()) {
  595. me.setValue(me.lastSelection);
  596. }
  597. } else {
  598. // For single-select, match the displayed value to a record and select it,
  599. // if it does not match a record then revert to the most recent selection.
  600. rec = me.findRecordByDisplay(value);
  601. if (rec) {
  602. me.select(rec);
  603. } else {
  604. me.setValue(me.lastSelection);
  605. }
  606. }
  607. }
  608. me.collapse();
  609. },
  610. onTypeAhead: function() {
  611. var me = this,
  612. displayField = me.displayField,
  613. record = me.store.findRecord(displayField, me.getRawValue()),
  614. boundList = me.getPicker(),
  615. newValue, len, selStart;
  616. if (record) {
  617. newValue = record.get(displayField);
  618. len = newValue.length;
  619. selStart = me.getRawValue().length;
  620. boundList.highlightItem(boundList.getNode(record));
  621. if (selStart !== 0 &amp;&amp; selStart !== len) {
  622. me.setRawValue(newValue);
  623. me.selectText(selStart, newValue.length);
  624. }
  625. }
  626. },
  627. // invoked when a different store is bound to this combo
  628. // than the original
  629. resetToDefault: Ext.emptyFn,
  630. beforeReset: function() {
  631. this.callParent();
  632. this.clearFilter();
  633. },
  634. onUnbindStore: function(store) {
  635. var picker = this.picker;
  636. if (!store &amp;&amp; picker) {
  637. picker.bindStore(null);
  638. }
  639. this.clearFilter();
  640. },
  641. onBindStore: function(store, initial) {
  642. var picker = this.picker;
  643. if (!initial) {
  644. this.resetToDefault();
  645. }
  646. if (picker) {
  647. picker.bindStore(store);
  648. }
  649. },
  650. getStoreListeners: function() {
  651. var me = this;
  652. return {
  653. beforeload: me.onBeforeLoad,
  654. clear: me.onClear,
  655. datachanged: me.onDataChanged,
  656. load: me.onLoad,
  657. exception: me.onException,
  658. remove: me.onRemove
  659. };
  660. },
  661. onBeforeLoad: function(){
  662. // If we're remote loading, the load mask will show which will trigger a deslectAll.
  663. // This selection change will trigger the collapse in onListSelectionChange. As such
  664. // we'll veto it for now and restore selection listeners when we've loaded.
  665. ++this.ignoreSelection;
  666. },
  667. onDataChanged: function() {
  668. var me = this;
  669. if (me.resizeComboToGrow()) {
  670. me.updateLayout();
  671. }
  672. },
  673. onClear: function() {
  674. var me = this;
  675. if (me.resizeComboToGrow()) {
  676. me.removingRecords = true;
  677. me.onDataChanged();
  678. }
  679. },
  680. onRemove: function() {
  681. var me = this;
  682. if (me.resizeComboToGrow()) {
  683. me.removingRecords = true;
  684. }
  685. },
  686. onException: function(){
  687. if (this.ignoreSelection &gt; 0) {
  688. --this.ignoreSelection;
  689. }
  690. this.collapse();
  691. },
  692. onLoad: function() {
  693. var me = this,
  694. value = me.value;
  695. if (me.ignoreSelection &gt; 0) {
  696. --me.ignoreSelection;
  697. }
  698. // If performing a remote query upon the raw value...
  699. if (me.rawQuery) {
  700. me.rawQuery = false;
  701. me.syncSelection();
  702. if (me.picker &amp;&amp; !me.picker.getSelectionModel().hasSelection()) {
  703. me.doAutoSelect();
  704. }
  705. }
  706. // If store initial load or triggerAction: 'all' trigger click.
  707. else {
  708. // Set the value on load
  709. if (me.value || me.value === 0) {
  710. me.setValue(me.value);
  711. } else {
  712. // There's no value.
  713. // Highlight the first item in the list if autoSelect: true
  714. if (me.store.getCount()) {
  715. me.doAutoSelect();
  716. } else {
  717. // assign whatever empty value we have to prevent change from firing
  718. me.setValue(me.value);
  719. }
  720. }
  721. }
  722. },
  723. <span id='Ext-form-field-ComboBox-method-doRawQuery'> /**
  724. </span> * @private
  725. * Execute the query with the raw contents within the textfield.
  726. */
  727. doRawQuery: function() {
  728. this.doQuery(this.getRawValue(), false, true);
  729. },
  730. <span id='Ext-form-field-ComboBox-method-doQuery'> /**
  731. </span> * Executes a query to filter the dropdown list. Fires the {@link #beforequery} event prior to performing the query
  732. * allowing the query action to be canceled if needed.
  733. *
  734. * @param {String} queryString The SQL query to execute
  735. * @param {Boolean} [forceAll=false] `true` to force the query to execute even if there are currently fewer characters in
  736. * the field than the minimum specified by the `{@link #minChars}` config option. It also clears any filter
  737. * previously saved in the current store.
  738. * @param {Boolean} [rawQuery=false] Pass as true if the raw typed value is being used as the query string. This causes the
  739. * resulting store load to leave the raw value undisturbed.
  740. * @return {Boolean} true if the query was permitted to run, false if it was cancelled by a {@link #beforequery}
  741. * handler.
  742. */
  743. doQuery: function(queryString, forceAll, rawQuery) {
  744. queryString = queryString || '';
  745. // store in object and pass by reference in 'beforequery'
  746. // so that client code can modify values.
  747. var me = this,
  748. qe = {
  749. query: queryString,
  750. forceAll: forceAll,
  751. combo: me,
  752. cancel: false
  753. },
  754. store = me.store,
  755. isLocalMode = me.queryMode === 'local',
  756. needsRefresh;
  757. if (me.fireEvent('beforequery', qe) === false || qe.cancel) {
  758. return false;
  759. }
  760. // get back out possibly modified values
  761. queryString = qe.query;
  762. forceAll = qe.forceAll;
  763. // query permitted to run
  764. if (forceAll || (queryString.length &gt;= me.minChars)) {
  765. // expand before starting query so LoadMask can position itself correctly
  766. me.expand();
  767. // make sure they aren't querying the same thing
  768. if (!me.queryCaching || me.lastQuery !== queryString) {
  769. me.lastQuery = queryString;
  770. if (isLocalMode) {
  771. // forceAll means no filtering - show whole dataset.
  772. store.suspendEvents();
  773. needsRefresh = me.clearFilter();
  774. if (queryString || !forceAll) {
  775. me.activeFilter = new Ext.util.Filter({
  776. root: 'data',
  777. property: me.displayField,
  778. value: queryString
  779. });
  780. store.filter(me.activeFilter);
  781. needsRefresh = true;
  782. } else {
  783. delete me.activeFilter;
  784. }
  785. store.resumeEvents();
  786. if (me.rendered &amp;&amp; needsRefresh) {
  787. me.getPicker().refresh();
  788. }
  789. } else {
  790. // Set flag for onLoad handling to know how the Store was loaded
  791. me.rawQuery = rawQuery;
  792. // In queryMode: 'remote', we assume Store filters are added by the developer as remote filters,
  793. // and these are automatically passed as params with every load call, so we do *not* call clearFilter.
  794. if (me.pageSize) {
  795. // if we're paging, we've changed the query so start at page 1.
  796. me.loadPage(1);
  797. } else {
  798. store.load({
  799. params: me.getParams(queryString)
  800. });
  801. }
  802. }
  803. }
  804. // Clear current selection if it does not match the current value in the field
  805. if (me.getRawValue() !== me.getDisplayValue()) {
  806. me.ignoreSelection++;
  807. me.picker.getSelectionModel().deselectAll();
  808. me.ignoreSelection--;
  809. }
  810. if (isLocalMode) {
  811. me.doAutoSelect();
  812. }
  813. if (me.typeAhead) {
  814. me.doTypeAhead();
  815. }
  816. }
  817. return true;
  818. },
  819. <span id='Ext-form-field-ComboBox-method-clearFilter'> /**
  820. </span> * Clears any previous filters applied by the combo to the store
  821. * @private
  822. * @return {Boolean} True if a filter was removed
  823. */
  824. clearFilter: function() {
  825. var store = this.store,
  826. filter = this.activeFilter,
  827. filters = store.filters,
  828. remaining;
  829. if (filter) {
  830. if (filters.getCount() &gt; 1) {
  831. // More than 1 existing filter
  832. filters.remove(filter);
  833. remaining = filters.getRange();
  834. }
  835. store.clearFilter(true);
  836. if (remaining) {
  837. store.filter(remaining);
  838. }
  839. }
  840. return !!filter;
  841. },
  842. loadPage: function(pageNum){
  843. this.store.loadPage(pageNum, {
  844. params: this.getParams(this.lastQuery)
  845. });
  846. },
  847. onPageChange: function(toolbar, newPage){
  848. /*
  849. * Return false here so we can call load ourselves and inject the query param.
  850. * We don't want to do this for every store load since the developer may load
  851. * the store through some other means so we won't add the query param.
  852. */
  853. this.loadPage(newPage);
  854. return false;
  855. },
  856. // private
  857. getParams: function(queryString) {
  858. var params = {},
  859. param = this.queryParam;
  860. if (param) {
  861. params[param] = queryString;
  862. }
  863. return params;
  864. },
  865. <span id='Ext-form-field-ComboBox-method-doAutoSelect'> /**
  866. </span> * @private
  867. * If the autoSelect config is true, and the picker is open, highlights the first item.
  868. */
  869. doAutoSelect: function() {
  870. var me = this,
  871. picker = me.picker,
  872. lastSelected, itemNode;
  873. if (picker &amp;&amp; me.autoSelect &amp;&amp; me.store.getCount() &gt; 0) {
  874. // Highlight the last selected item and scroll it into view
  875. lastSelected = picker.getSelectionModel().lastSelected;
  876. itemNode = picker.getNode(lastSelected || 0);
  877. if (itemNode) {
  878. picker.highlightItem(itemNode);
  879. picker.listEl.scrollChildIntoView(itemNode, false);
  880. }
  881. }
  882. },
  883. doTypeAhead: function() {
  884. if (!this.typeAheadTask) {
  885. this.typeAheadTask = new Ext.util.DelayedTask(this.onTypeAhead, this);
  886. }
  887. if (this.lastKey != Ext.EventObject.BACKSPACE &amp;&amp; this.lastKey != Ext.EventObject.DELETE) {
  888. this.typeAheadTask.delay(this.typeAheadDelay);
  889. }
  890. },
  891. onTriggerClick: function() {
  892. var me = this;
  893. if (!me.readOnly &amp;&amp; !me.disabled) {
  894. if (me.isExpanded) {
  895. me.collapse();
  896. } else {
  897. me.onFocus({});
  898. if (me.triggerAction === 'all') {
  899. me.doQuery(me.allQuery, true);
  900. } else {
  901. me.doQuery(me.getRawValue(), false, true);
  902. }
  903. }
  904. me.inputEl.focus();
  905. }
  906. },
  907. // store the last key and doQuery if relevant
  908. onKeyUp: function(e, t) {
  909. var me = this,
  910. key = e.getKey();
  911. if (!me.readOnly &amp;&amp; !me.disabled &amp;&amp; me.editable) {
  912. me.lastKey = key;
  913. // we put this in a task so that we can cancel it if a user is
  914. // in and out before the queryDelay elapses
  915. // perform query w/ any normal key or backspace or delete
  916. if (!e.isSpecialKey() || key == e.BACKSPACE || key == e.DELETE) {
  917. me.doQueryTask.delay(me.queryDelay);
  918. }
  919. }
  920. if (me.enableKeyEvents) {
  921. me.callParent(arguments);
  922. }
  923. },
  924. initEvents: function() {
  925. var me = this;
  926. me.callParent();
  927. /*
  928. * Setup keyboard handling. If enableKeyEvents is true, we already have
  929. * a listener on the inputEl for keyup, so don't create a second.
  930. */
  931. if (!me.enableKeyEvents) {
  932. me.mon(me.inputEl, 'keyup', me.onKeyUp, me);
  933. }
  934. },
  935. onDestroy: function() {
  936. this.bindStore(null);
  937. this.callParent();
  938. },
  939. // The picker (the dropdown) must have its zIndex managed by the same ZIndexManager which is
  940. // providing the zIndex of our Container.
  941. onAdded: function() {
  942. var me = this;
  943. me.callParent(arguments);
  944. if (me.picker) {
  945. me.picker.ownerCt = me.up('[floating]');
  946. me.picker.registerWithOwnerCt();
  947. }
  948. },
  949. createPicker: function() {
  950. var me = this,
  951. picker,
  952. pickerCfg = Ext.apply({
  953. xtype: 'boundlist',
  954. pickerField: me,
  955. selModel: {
  956. mode: me.multiSelect ? 'SIMPLE' : 'SINGLE'
  957. },
  958. floating: true,
  959. hidden: true,
  960. store: me.store,
  961. displayField: me.displayField,
  962. focusOnToFront: false,
  963. pageSize: me.pageSize,
  964. tpl: me.tpl
  965. }, me.listConfig, me.defaultListConfig);
  966. picker = me.picker = Ext.widget(pickerCfg);
  967. if (me.pageSize) {
  968. picker.pagingToolbar.on('beforechange', me.onPageChange, me);
  969. }
  970. me.mon(picker, {
  971. itemclick: me.onItemClick,
  972. refresh: me.onListRefresh,
  973. scope: me
  974. });
  975. me.mon(picker.getSelectionModel(), {
  976. beforeselect: me.onBeforeSelect,
  977. beforedeselect: me.onBeforeDeselect,
  978. selectionchange: me.onListSelectionChange,
  979. scope: me
  980. });
  981. return picker;
  982. },
  983. alignPicker: function(){
  984. var me = this,
  985. picker = me.getPicker(),
  986. heightAbove = me.getPosition()[1] - Ext.getBody().getScroll().top,
  987. heightBelow = Ext.Element.getViewHeight() - heightAbove - me.getHeight(),
  988. space = Math.max(heightAbove, heightBelow);
  989. // Allow the picker to height itself naturally.
  990. if (picker.height) {
  991. delete picker.height;
  992. picker.updateLayout();
  993. }
  994. // Then ensure that vertically, the dropdown will fit into the space either above or below the inputEl.
  995. if (picker.getHeight() &gt; space - 5) {
  996. picker.setHeight(space - 5); // have some leeway so we aren't flush against
  997. }
  998. me.callParent();
  999. },
  1000. onListRefresh: function() {
  1001. this.alignPicker();
  1002. this.syncSelection();
  1003. },
  1004. onItemClick: function(picker, record){
  1005. /*
  1006. * If we're doing single selection, the selection change events won't fire when
  1007. * clicking on the selected element. Detect it here.
  1008. */
  1009. var me = this,
  1010. selection = me.picker.getSelectionModel().getSelection(),
  1011. valueField = me.valueField;
  1012. if (!me.multiSelect &amp;&amp; selection.length) {
  1013. if (record.get(valueField) === selection[0].get(valueField)) {
  1014. // Make sure we also update the display value if it's only partial
  1015. me.displayTplData = [record.data];
  1016. me.setRawValue(me.getDisplayValue());
  1017. me.collapse();
  1018. }
  1019. }
  1020. },
  1021. onBeforeSelect: function(list, record) {
  1022. return this.fireEvent('beforeselect', this, record, record.index);
  1023. },
  1024. onBeforeDeselect: function(list, record) {
  1025. return this.fireEvent('beforedeselect', this, record, record.index);
  1026. },
  1027. onListSelectionChange: function(list, selectedRecords) {
  1028. var me = this,
  1029. isMulti = me.multiSelect,
  1030. hasRecords = selectedRecords.length &gt; 0;
  1031. // Only react to selection if it is not called from setValue, and if our list is
  1032. // expanded (ignores changes to the selection model triggered elsewhere)
  1033. if (!me.ignoreSelection &amp;&amp; me.isExpanded) {
  1034. if (!isMulti) {
  1035. Ext.defer(me.collapse, 1, me);
  1036. }
  1037. /*
  1038. * Only set the value here if we're in multi selection mode or we have
  1039. * a selection. Otherwise setValue will be called with an empty value
  1040. * which will cause the change event to fire twice.
  1041. */
  1042. if (isMulti || hasRecords) {
  1043. me.setValue(selectedRecords, false);
  1044. }
  1045. if (hasRecords) {
  1046. me.fireEvent('select', me, selectedRecords);
  1047. }
  1048. me.inputEl.focus();
  1049. }
  1050. },
  1051. <span id='Ext-form-field-ComboBox-method-onExpand'> /**
  1052. </span> * @private
  1053. * Enables the key nav for the BoundList when it is expanded.
  1054. */
  1055. onExpand: function() {
  1056. var me = this,
  1057. keyNav = me.listKeyNav,
  1058. selectOnTab = me.selectOnTab,
  1059. picker = me.getPicker();
  1060. // Handle BoundList navigation from the input field. Insert a tab listener specially to enable selectOnTab.
  1061. if (keyNav) {
  1062. keyNav.enable();
  1063. } else {
  1064. keyNav = me.listKeyNav = new Ext.view.BoundListKeyNav(this.inputEl, {
  1065. boundList: picker,
  1066. forceKeyDown: true,
  1067. tab: function(e) {
  1068. if (selectOnTab) {
  1069. this.selectHighlighted(e);
  1070. me.triggerBlur();
  1071. }
  1072. // Tab key event is allowed to propagate to field
  1073. return true;
  1074. }
  1075. });
  1076. }
  1077. // While list is expanded, stop tab monitoring from Ext.form.field.Trigger so it doesn't short-circuit selectOnTab
  1078. if (selectOnTab) {
  1079. me.ignoreMonitorTab = true;
  1080. }
  1081. Ext.defer(keyNav.enable, 1, keyNav); //wait a bit so it doesn't react to the down arrow opening the picker
  1082. me.inputEl.focus();
  1083. },
  1084. <span id='Ext-form-field-ComboBox-method-onCollapse'> /**
  1085. </span> * @private
  1086. * Disables the key nav for the BoundList when it is collapsed.
  1087. */
  1088. onCollapse: function() {
  1089. var me = this,
  1090. keyNav = me.listKeyNav;
  1091. if (keyNav) {
  1092. keyNav.disable();
  1093. me.ignoreMonitorTab = false;
  1094. }
  1095. },
  1096. <span id='Ext-form-field-ComboBox-method-select'> /**
  1097. </span> * Selects an item by a {@link Ext.data.Model Model}, or by a key value.
  1098. * @param {Object} r
  1099. */
  1100. select: function(r) {
  1101. this.setValue(r, true);
  1102. },
  1103. <span id='Ext-form-field-ComboBox-method-findRecord'> /**
  1104. </span> * Finds the record by searching for a specific field/value combination.
  1105. * @param {String} field The name of the field to test.
  1106. * @param {Object} value The value to match the field against.
  1107. * @return {Ext.data.Model} The matched record or false.
  1108. */
  1109. findRecord: function(field, value) {
  1110. var ds = this.store,
  1111. idx = ds.findExact(field, value);
  1112. return idx !== -1 ? ds.getAt(idx) : false;
  1113. },
  1114. <span id='Ext-form-field-ComboBox-method-findRecordByValue'> /**
  1115. </span> * Finds the record by searching values in the {@link #valueField}.
  1116. * @param {Object} value The value to match the field against.
  1117. * @return {Ext.data.Model} The matched record or false.
  1118. */
  1119. findRecordByValue: function(value) {
  1120. return this.findRecord(this.valueField, value);
  1121. },
  1122. <span id='Ext-form-field-ComboBox-method-findRecordByDisplay'> /**
  1123. </span> * Finds the record by searching values in the {@link #displayField}.
  1124. * @param {Object} value The value to match the field against.
  1125. * @return {Ext.data.Model} The matched record or false.
  1126. */
  1127. findRecordByDisplay: function(value) {
  1128. return this.findRecord(this.displayField, value);
  1129. },
  1130. <span id='Ext-form-field-ComboBox-method-setValue'> /**
  1131. </span> * Sets the specified value(s) into the field. For each value, if a record is found in the {@link #store} that
  1132. * matches based on the {@link #valueField}, then that record's {@link #displayField} will be displayed in the
  1133. * field. If no match is found, and the {@link #valueNotFoundText} config option is defined, then that will be
  1134. * displayed as the default field text. Otherwise a blank value will be shown, although the value will still be set.
  1135. * @param {String/String[]} value The value(s) to be set. Can be either a single String or {@link Ext.data.Model},
  1136. * or an Array of Strings or Models.
  1137. * @return {Ext.form.field.Field} this
  1138. */
  1139. setValue: function(value, doSelect) {
  1140. var me = this,
  1141. valueNotFoundText = me.valueNotFoundText,
  1142. inputEl = me.inputEl,
  1143. i, len, record,
  1144. dataObj,
  1145. matchedRecords = [],
  1146. displayTplData = [],
  1147. processedValue = [];
  1148. if (me.store.loading) {
  1149. // Called while the Store is loading. Ensure it is processed by the onLoad method.
  1150. me.value = value;
  1151. me.setHiddenValue(me.value);
  1152. return me;
  1153. }
  1154. // This method processes multi-values, so ensure value is an array.
  1155. value = Ext.Array.from(value);
  1156. // Loop through values, matching each from the Store, and collecting matched records
  1157. for (i = 0, len = value.length; i &lt; len; i++) {
  1158. record = value[i];
  1159. if (!record || !record.isModel) {
  1160. record = me.findRecordByValue(record);
  1161. }
  1162. // record found, select it.
  1163. if (record) {
  1164. matchedRecords.push(record);
  1165. displayTplData.push(record.data);
  1166. processedValue.push(record.get(me.valueField));
  1167. }
  1168. // record was not found, this could happen because
  1169. // store is not loaded or they set a value not in the store
  1170. else {
  1171. // If we are allowing insertion of values not represented in the Store, then push the value and
  1172. // create a fake record data object to push as a display value for use by the displayTpl
  1173. if (!me.forceSelection) {
  1174. processedValue.push(value[i]);
  1175. dataObj = {};
  1176. dataObj[me.displayField] = value[i];
  1177. displayTplData.push(dataObj);
  1178. // TODO: Add config to create new records on selection of a value that has no match in the Store
  1179. }
  1180. // Else, if valueNotFoundText is defined, display it, otherwise display nothing for this value
  1181. else if (Ext.isDefined(valueNotFoundText)) {
  1182. displayTplData.push(valueNotFoundText);
  1183. }
  1184. }
  1185. }
  1186. // Set the value of this field. If we are multiselecting, then that is an array.
  1187. me.setHiddenValue(processedValue);
  1188. me.value = me.multiSelect ? processedValue : processedValue[0];
  1189. if (!Ext.isDefined(me.value)) {
  1190. me.value = null;
  1191. }
  1192. me.displayTplData = displayTplData; //store for getDisplayValue method
  1193. me.lastSelection = me.valueModels = matchedRecords;
  1194. if (inputEl &amp;&amp; me.emptyText &amp;&amp; !Ext.isEmpty(value)) {
  1195. inputEl.removeCls(me.emptyCls);
  1196. }
  1197. // Calculate raw value from the collection of Model data
  1198. me.setRawValue(me.getDisplayValue());
  1199. me.checkChange();
  1200. if (doSelect !== false) {
  1201. me.syncSelection();
  1202. }
  1203. me.applyEmptyText();
  1204. return me;
  1205. },
  1206. <span id='Ext-form-field-ComboBox-method-setHiddenValue'> /**
  1207. </span> * @private
  1208. * Set the value of {@link #hiddenDataEl}
  1209. * Dynamically adds and removes input[type=hidden] elements
  1210. */
  1211. setHiddenValue: function(values){
  1212. var me = this,
  1213. name = me.hiddenName,
  1214. i,
  1215. dom, childNodes, input, valueCount, childrenCount;
  1216. if (!me.hiddenDataEl || !name) {
  1217. return;
  1218. }
  1219. values = Ext.Array.from(values);
  1220. dom = me.hiddenDataEl.dom;
  1221. childNodes = dom.childNodes;
  1222. input = childNodes[0];
  1223. valueCount = values.length;
  1224. childrenCount = childNodes.length;
  1225. if (!input &amp;&amp; valueCount &gt; 0) {
  1226. me.hiddenDataEl.update(Ext.DomHelper.markup({
  1227. tag: 'input',
  1228. type: 'hidden',
  1229. name: name
  1230. }));
  1231. childrenCount = 1;
  1232. input = dom.firstChild;
  1233. }
  1234. while (childrenCount &gt; valueCount) {
  1235. dom.removeChild(childNodes[0]);
  1236. -- childrenCount;
  1237. }
  1238. while (childrenCount &lt; valueCount) {
  1239. dom.appendChild(input.cloneNode(true));
  1240. ++ childrenCount;
  1241. }
  1242. for (i = 0; i &lt; valueCount; i++) {
  1243. childNodes[i].value = values[i];
  1244. }
  1245. },
  1246. <span id='Ext-form-field-ComboBox-method-getDisplayValue'> /**
  1247. </span> * @private Generates the string value to be displayed in the text field for the currently stored value
  1248. */
  1249. getDisplayValue: function() {
  1250. return this.displayTpl.apply(this.displayTplData);
  1251. },
  1252. getValue: function() {
  1253. // If the user has not changed the raw field value since a value was selected from the list,
  1254. // then return the structured value from the selection. If the raw field value is different
  1255. // than what would be displayed due to selection, return that raw value.
  1256. var me = this,
  1257. picker = me.picker,
  1258. rawValue = me.getRawValue(), //current value of text field
  1259. value = me.value; //stored value from last selection or setValue() call
  1260. if (me.getDisplayValue() !== rawValue) {
  1261. value = rawValue;
  1262. me.value = me.displayTplData = me.valueModels = null;
  1263. if (picker) {
  1264. me.ignoreSelection++;
  1265. picker.getSelectionModel().deselectAll();
  1266. me.ignoreSelection--;
  1267. }
  1268. }
  1269. return value;
  1270. },
  1271. getSubmitValue: function() {
  1272. return this.getValue();
  1273. },
  1274. isEqual: function(v1, v2) {
  1275. var fromArray = Ext.Array.from,
  1276. i, len;
  1277. v1 = fromArray(v1);
  1278. v2 = fromArray(v2);
  1279. len = v1.length;
  1280. if (len !== v2.length) {
  1281. return false;
  1282. }
  1283. for(i = 0; i &lt; len; i++) {
  1284. if (v2[i] !== v1[i]) {
  1285. return false;
  1286. }
  1287. }
  1288. return true;
  1289. },
  1290. <span id='Ext-form-field-ComboBox-method-clearValue'> /**
  1291. </span> * Clears any value currently set in the ComboBox.
  1292. */
  1293. clearValue: function() {
  1294. this.setValue([]);
  1295. },
  1296. <span id='Ext-form-field-ComboBox-method-syncSelection'> /**
  1297. </span> * @private Synchronizes the selection in the picker to match the current value of the combobox.
  1298. */
  1299. syncSelection: function() {
  1300. var me = this,
  1301. picker = me.picker,
  1302. selection, selModel,
  1303. values = me.valueModels || [],
  1304. vLen = values.length, v, value;
  1305. if (picker) {
  1306. // From the value, find the Models that are in the store's current data
  1307. selection = [];
  1308. for (v = 0; v &lt; vLen; v++) {
  1309. value = values[v];
  1310. if (value &amp;&amp; value.isModel &amp;&amp; me.store.indexOf(value) &gt;= 0) {
  1311. selection.push(value);
  1312. }
  1313. }
  1314. // Update the selection to match
  1315. me.ignoreSelection++;
  1316. selModel = picker.getSelectionModel();
  1317. selModel.deselectAll();
  1318. if (selection.length) {
  1319. selModel.select(selection);
  1320. }
  1321. me.ignoreSelection--;
  1322. }
  1323. },
  1324. onEditorTab: function(e){
  1325. var keyNav = this.listKeyNav;
  1326. if (this.selectOnTab &amp;&amp; keyNav) {
  1327. keyNav.selectHighlighted(e);
  1328. }
  1329. }
  1330. });
  1331. </pre>
  1332. </body>
  1333. </html>