sencha touch 带本地搜索功能的selectfield(选择插件)

带本地搜索功能的选择插件,效果图:
sencha touch 带本地搜索功能的selectfield(选择插件)

sencha touch 带本地搜索功能的selectfield(选择插件)

在使用selectfield的过程中,数据过大时,数据加载缓慢,没有模糊查询用户体验也不好,

在selectfield的基础上上稍作修改而成,使用方式同selectfield,代码:

  1 Ext.define('ux.field.Select', {

  2     extend: 'Ext.field.Text',

  3     xtype: 'uxSelectfield',

  4     alternateClassName: 'ux.form.Select',

  5     requires: [

  6         'Ext.Panel',

  7         'Ext.picker.Picker',

  8         'Ext.data.Store',

  9         'Ext.data.StoreManager',

 10         'Ext.dataview.List'

 11     ],

 12 

 13     /**

 14      * @event change

 15      * Fires when an option selection has changed

 16      * @param {Ext.field.Select} this

 17      * @param {Mixed} newValue The new value

 18      * @param {Mixed} oldValue The old value

 19      */

 20 

 21     /**

 22      * @event focus

 23      * Fires when this field receives input focus. This happens both when you tap on the field and when you focus on the field by using

 24      * 'next' or 'tab' on a keyboard.

 25      *

 26      * Please note that this event is not very reliable on Android. For example, if your Select field is second in your form panel,

 27      * you cannot use the Next button to get to this select field. This functionality works as expected on iOS.

 28      * @param {Ext.field.Select} this This field

 29      * @param {Ext.event.Event} e

 30      */

 31 

 32     config: {

 33         /**

 34          * @cfg

 35          * @inheritdoc

 36          */

 37         ui: 'select',

 38 

 39         /**

 40          * @cfg {Boolean} useClearIcon

 41          * @hide

 42          */

 43 

 44         /**

 45          * @cfg {String/Number} valueField The underlying {@link Ext.data.Field#name data value name} (or numeric Array index) to bind to this

 46          * Select control.

 47          * @accessor

 48          */

 49         valueField: 'value',

 50 

 51         /**

 52          * @cfg {String/Number} displayField The underlying {@link Ext.data.Field#name data value name} (or numeric Array index) to bind to this

 53          * Select control. This resolved value is the visibly rendered value of the available selection options.

 54          * @accessor

 55          */

 56         displayField: 'text',

 57 

 58         /**

 59          * @cfg {Ext.data.Store/Object/String} store The store to provide selection options data.

 60          * Either a Store instance, configuration object or store ID.

 61          * @accessor

 62          */

 63         store: null,

 64 

 65         /**

 66          * @cfg {Array} options An array of select options.

 67          *

 68          *     [

 69          *         {text: 'First Option',  value: 'first'},

 70          *         {text: 'Second Option', value: 'second'},

 71          *         {text: 'Third Option',  value: 'third'}

 72          *     ]

 73          *

 74          * __Note:__ Option object member names should correspond with defined {@link #valueField valueField} and {@link #displayField displayField} values.

 75          * This config will be ignored if a {@link #store store} instance is provided.

 76          * @accessor

 77          */

 78         options: null,

 79 

 80         /**

 81          * @cfg {String} hiddenName Specify a `hiddenName` if you're using the {@link Ext.form.Panel#standardSubmit standardSubmit} option.

 82          * This name will be used to post the underlying value of the select to the server.

 83          * @accessor

 84          */

 85         hiddenName: null,

 86 

 87         /**

 88          * @cfg {Object} component

 89          * @accessor

 90          * @hide

 91          */

 92         component: {

 93             useMask: true

 94         },

 95 

 96         /**

 97          * @cfg {Boolean} clearIcon

 98          * @hide

 99          * @accessor

100          */

101         clearIcon: false,

102 

103         /**

104          * 请勿改动此配置

105          */

106         usePicker: false,

107 

108         /**

109          * @cfg {Boolean} autoSelect

110          * `true` to auto select the first value in the {@link #store} or {@link #options} when they are changed. Only happens when

111          * the {@link #value} is set to `null`.

112          */

113         autoSelect: true,

114 

115         /**

116          * @cfg {Object} defaultPhonePickerConfig

117          * The default configuration for the picker component when you are on a phone.

118          */

119         defaultPhonePickerConfig: null,

120 

121         /**

122          * @cfg {Object} defaultTabletPickerConfig

123          * The default configuration for the picker component when you are on a tablet.

124          */

125         defaultTabletPickerConfig: null,

126 

127         /**

128          * @cfg

129          * @inheritdoc

130          */

131         name: 'picker',

132 

133         /**

134          * @cfg {String} pickerSlotAlign

135          * The alignment of text in the picker created by this Select

136          * @private

137          */

138         pickerSlotAlign: 'center'

139     },

140 

141     platformConfig: [

142         {

143             theme: ['Windows'],

144             pickerSlotAlign: 'left'

145         },

146         {

147             theme: ['Tizen'],

148             usePicker: false

149         }

150     ],

151 

152     // @private

153     initialize: function () {

154         var me = this,

155             component = me.getComponent();

156 

157         me.callParent();

158 

159         component.on({

160             scope: me,

161             masktap: 'onMaskTap'

162         });

163 

164         component.doMaskTap = Ext.emptyFn;

165 

166         if (Ext.browser.is.AndroidStock2) {

167             component.input.dom.disabled = true;

168         }

169 

170         if (Ext.theme.is.Blackberry) {

171             this.label.on({

172                 scope: me,

173                 tap: "onFocus"

174             });

175         }

176     },

177 

178     getElementConfig: function () {

179         if (Ext.theme.is.Blackberry) {

180             var prefix = Ext.baseCSSPrefix;

181 

182             return {

183                 reference: 'element',

184                 className: 'x-container',

185                 children: [

186                     {

187                         reference: 'innerElement',

188                         cls: prefix + 'component-outer',

189                         children: [

190                             {

191                                 reference: 'label',

192                                 cls: prefix + 'form-label',

193                                 children: [{

194                                     reference: 'labelspan',

195                                     tag: 'span'

196                                 }]

197                             }

198                         ]

199                     }

200                 ]

201             };

202         } else {

203             return this.callParent(arguments);

204         }

205     },

206 

207     /**

208      * @private

209      */

210     updateDefaultPhonePickerConfig: function (newConfig) {

211         var picker = this.picker;

212         if (picker) {

213             picker.setConfig(newConfig);

214         }

215     },

216 

217     /**

218      * @private

219      */

220     updateDefaultTabletPickerConfig: function (newConfig) {

221         var listPanel = this.listPanel;

222         if (listPanel) {

223             listPanel.setConfig(newConfig);

224         }

225     },

226 

227     /**

228      * @private

229      * Checks if the value is `auto`. If it is, it only uses the picker if the current device type

230      * is a phone.

231      */

232     applyUsePicker: function (usePicker) {

233         if (usePicker == "auto") {

234             usePicker = (Ext.os.deviceType == 'Phone');

235         }

236 

237         return Boolean(usePicker);

238     },

239 

240     syncEmptyCls: Ext.emptyFn,

241 

242     /**

243      * @private

244      */

245     applyValue: function (value) {

246         var record = value,

247             index, store;

248 

249         //we call this so that the options configruation gets intiailized, so that a store exists, and we can

250         //find the correct value

251         this.getOptions();

252 

253         store = this.getStore();

254 

255         if ((value != undefined && !value.isModel) && store) {

256             index = store.find(this.getValueField(), value, null, null, null, true);

257 

258             if (index == -1) {

259                 index = store.find(this.getDisplayField(), value, null, null, null, true);

260             }

261 

262             record = store.getAt(index);

263         }

264 

265         return record;

266     },

267 

268     updateValue: function (newValue, oldValue) {

269         this.record = newValue;

270         this.callParent([(newValue && newValue.isModel) ? newValue.get(this.getDisplayField()) : '']);

271     },

272 

273     getValue: function () {

274         var record = this.record;

275         return (record && record.isModel) ? record.get(this.getValueField()) : null;

276     },

277 

278     /**

279      * Returns the current selected {@link Ext.data.Model record} instance selected in this field.

280      * @return {Ext.data.Model} the record.

281      */

282     getRecord: function () {

283         return this.record;

284     },

285 

286     // @private

287     getPhonePicker: function () {

288         var config = this.getDefaultPhonePickerConfig();

289 

290         if (!this.picker) {

291             this.picker = Ext.create('Ext.picker.Picker', Ext.apply({

292                 slots: [

293                     {

294                         align: this.getPickerSlotAlign(),

295                         name: this.getName(),

296                         valueField: this.getValueField(),

297                         displayField: this.getDisplayField(),

298                         value: this.getValue(),

299                         store: this.getStore()

300                     }

301                 ],

302                 listeners: {

303                     change: this.onPickerChange,

304                     scope: this

305                 }

306             }, config));

307         }

308 

309         return this.picker;

310     },

311 

312     // @private

313     getTabletPicker: function () {

314         var config = this.getDefaultTabletPickerConfig();

315 

316         if (!this.listPanel) {

317             this.listPanel = Ext.create('Ext.Panel', Ext.apply({

318                 left: 0,

319                 top: 0,

320                 modal: true,

321                 cls: Ext.baseCSSPrefix + 'select-overlay',

322                 layout: 'fit',

323                 hideOnMaskTap: true,

324                 width: Ext.os.is.Phone ? '14em' : '18em',

325                 height: (Ext.os.is.BlackBerry && Ext.os.version.getMajor() === 10) ? '12em' : (Ext.os.is.Phone ? '12.5em' : '22em'),

326                 items: [{

327                     xtype: 'toolbar',

328                     docked: 'top',

329                     items: [

330                         //新增的搜索栏,用于支持模糊查询

331                         {

332                             xtype: 'searchfield',

333                             placeHolder: '请输入关键词',

334                             width:'100%',

335                             clearIcon:false,

336                             listeners: {

337                                 keyup: 'onSearch',

338                                 scope: this

339                             }

340                         }

341                     ]

342                 }, {

343                     xtype: 'list',

344                     store: this.getStore(),

345                     itemTpl: '<span class="x-list-label">{' + this.getDisplayField() + ':htmlEncode}</span>',

346                     listeners: {

347                         select: this.onListSelect,

348                         itemtap: this.onListTap,

349                         scope: this

350                     }

351                 }]

352             }, config));

353         }

354 

355         return this.listPanel;

356     },

357     //进行模糊查询

358     onSearchKeyUp: function (value) {

359         //得到数据仓库和搜索关键词

360         var store = this.getStore();

361 

362         //如果是新的关键词,则清除过滤

363         store.clearFilter(!!value);

364         //检查值是否存在

365         if (value) {

366             //the user could have entered spaces, so we must split them so we can loop through them all

367             var key = this.getDisplayField(),

368              searches = value.split(','),

369                 regexps = [],

370                 //获取现实值的name

371                 i, regex;

372 

373             //loop them all

374             for (i = 0; i < searches.length; i++) {

375                 //if it is nothing, continue

376                 if (!searches[i]) continue;

377 

378                 regex = searches[i].trim();

379                 regex = regex.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");

380 

381                 //if found, create a new regular expression which is case insenstive

382                 regexps.push(new RegExp(regex.trim(), 'i'));

383             }

384 

385             //now filter the store by passing a method

386             //the passed method will be called for each record in the store

387             store.filter(function (record) {

388                 var matched = [];

389 

390                 //loop through each of the regular expressions

391                 for (i = 0; i < regexps.length; i++) {

392                     var search = regexps[i],

393                         didMatch = search.test(record.get(key));

394 

395                     //if it matched the first or last name, push it into the matches array

396                     matched.push(didMatch);

397                 }

398 

399                 return (regexps.length && matched.indexOf(true) !== -1);

400             });

401         }

402     },

403     //进行模糊查询

404     onSearch: function (field) {

405         this.onSearchKeyUp(field.getValue());

406     },

407     // @private

408     onMaskTap: function () {

409         this.onFocus();

410 

411         return false;

412     },

413 

414     /**

415      * Shows the picker for the select field, whether that is a {@link Ext.picker.Picker} or a simple

416      * {@link Ext.List list}.

417      */

418     showPicker: function () {

419         var me = this,

420             store = me.getStore(),

421             value = me.getValue();

422 

423         //check if the store is empty, if it is, return

424         if (!store || store.getCount() === 0) {

425             return;

426         }

427         if (me.getReadOnly()) {

428             return;

429         }

430         me.isFocused = true;

431 

432         if (me.getUsePicker()) {

433             var picker = me.getPhonePicker(),

434                 name = me.getName(),

435                 pickerValue = {};

436 

437             pickerValue[name] = value;

438             picker.setValue(pickerValue);

439 

440             if (!picker.getParent()) {

441                 Ext.Viewport.add(picker);

442             }

443 

444             picker.show();

445         } else {

446             //先过滤一下避免加载过慢

447             var record = this.getRecord(),

448                 text='请搜索';

449             if (record) {

450                  text = record.get(this.getDisplayField());

451             }

452             this.onSearchKeyUp(text);

453 

454             var listPanel = me.getTabletPicker(),

455                 list = listPanel.down('list'),

456                 index, record;

457 

458             if (!listPanel.getParent()) {

459                 Ext.Viewport.add(listPanel);

460             }

461             //为搜索栏赋值

462             listPanel.down('searchfield').setValue(text);

463             listPanel.showBy(me.getComponent(), null);

464             if (value || me.getAutoSelect()) {

465                 store = list.getStore();

466                 index = store.find(me.getValueField(), value, null, null, null, true);

467                 record = store.getAt(index);

468 

469                 if (record) {

470                     list.select(record, null, true);

471                 }

472             }

473         }

474     },

475 

476     // @private

477     onListSelect: function (item, record) {

478         var me = this;

479         if (record) {

480             me.setValue(record);

481         }

482     },

483 

484     onListTap: function () {

485         this.listPanel.hide({

486             type: 'fade',

487             out: true,

488             scope: this

489         });

490     },

491 

492     // @private

493     onPickerChange: function (picker, value) {

494         var me = this,

495             newValue = value[me.getName()],

496             store = me.getStore(),

497             index = store.find(me.getValueField(), newValue, null, null, null, true),

498             record = store.getAt(index);

499 

500         me.setValue(record);

501     },

502 

503     onChange: function (component, newValue, oldValue) {

504         var me = this,

505             store = me.getStore(),

506             index = (store) ? store.find(me.getDisplayField(), oldValue, null, null, null, true) : -1,

507             valueField = me.getValueField(),

508             record = (store) ? store.getAt(index) : null;

509 

510         oldValue = (record) ? record.get(valueField) : null;

511 

512         me.fireEvent('change', me, me.getValue(), oldValue);

513     },

514 

515     /**

516      * Updates the underlying `<options>` list with new values.

517      *

518      * @param {Array} newOptions An array of options configurations to insert or append.

519      *

520      *     selectBox.setOptions([

521      *         {text: 'First Option',  value: 'first'},

522      *         {text: 'Second Option', value: 'second'},

523      *         {text: 'Third Option',  value: 'third'}

524      *     ]).setValue('third');

525      *

526      * __Note:__ option object member names should correspond with defined {@link #valueField valueField} and

527      * {@link #displayField displayField} values.

528      *

529      * @return {Ext.field.Select} this

530      */

531     updateOptions: function (newOptions) {

532         var store = this.getStore();

533 

534         if (!store) {

535             this.setStore(true);

536             store = this._store;

537         }

538 

539         if (!newOptions) {

540             store.clearData();

541         }

542         else {

543             store.setData(newOptions);

544             this.onStoreDataChanged(store);

545         }

546         return this;

547     },

548 

549     applyStore: function (store) {

550         if (store === true) {

551             store = Ext.create('Ext.data.Store', {

552                 fields: [this.getValueField(), this.getDisplayField()],

553                 autoDestroy: true

554             });

555         }

556 

557         if (store) {

558             store = Ext.data.StoreManager.lookup(store);

559 

560             store.on({

561                 scope: this,

562                 addrecords: 'onStoreDataChanged',

563                 removerecords: 'onStoreDataChanged',

564                 updaterecord: 'onStoreDataChanged',

565                 refresh: 'onStoreDataChanged'

566             });

567         }

568 

569         return store;

570     },

571 

572     updateStore: function (newStore) {

573         if (newStore) {

574             this.onStoreDataChanged(newStore);

575         }

576 

577         if (this.getUsePicker() && this.picker) {

578             this.picker.down('pickerslot').setStore(newStore);

579         } else if (this.listPanel) {

580             this.listPanel.down('dataview').setStore(newStore);

581         }

582     },

583 

584     /**

585      * Called when the internal {@link #store}'s data has changed.

586      */

587     onStoreDataChanged: function (store) {

588         var initialConfig = this.getInitialConfig(),

589             value = this.getValue();

590 

591         if (value || value == 0) {

592             this.updateValue(this.applyValue(value));

593         }

594 

595         if (this.getValue() === null) {

596             if (initialConfig.hasOwnProperty('value')) {

597                 this.setValue(initialConfig.value);

598             }

599 

600             if (this.getValue() === null && this.getAutoSelect()) {

601                 if (store.getCount() > 0) {

602                     this.setValue(store.getAt(0));

603                 }

604             }

605         }

606     },

607 

608     /**

609      * @private

610      */

611     doSetDisabled: function (disabled) {

612         var component = this.getComponent();

613         if (component) {

614             component.setDisabled(disabled);

615         }

616         Ext.Component.prototype.doSetDisabled.apply(this, arguments);

617     },

618 

619     /**

620      * @private

621      */

622     setDisabled: function () {

623         Ext.Component.prototype.setDisabled.apply(this, arguments);

624     },

625 

626     // @private

627     updateLabelWidth: function () {

628         if (Ext.theme.is.Blackberry) {

629             return;

630         } else {

631             this.callParent(arguments);

632         }

633     },

634 

635     // @private

636     updateLabelAlign: function () {

637         if (Ext.theme.is.Blackberry) {

638             return;

639         } else {

640             this.callParent(arguments);

641         }

642     },

643 

644     /**

645      * Resets the Select field to the value of the first record in the store.

646      * @return {Ext.field.Select} this

647      * @chainable

648      */

649     reset: function () {

650         var me = this,

651             record;

652 

653         if (me.getAutoSelect()) {

654             var store = me.getStore();

655 

656             record = (me.originalValue) ? me.originalValue : store.getAt(0);

657         } else {

658             var usePicker = me.getUsePicker(),

659                 picker = usePicker ? me.picker : me.listPanel;

660 

661             if (picker) {

662                 picker = picker.child(usePicker ? 'pickerslot' : 'dataview');

663 

664                 picker.deselectAll();

665             }

666 

667             record = null;

668         }

669 

670         me.setValue(record);

671 

672         return me;

673     },

674 

675     onFocus: function (e) {

676         if (this.getDisabled()) {

677             return false;

678         }

679         var component = this.getComponent();

680         this.fireEvent('focus', this, e);

681 

682         if (Ext.os.is.Android4) {

683             component.input.dom.focus();

684         }

685         component.input.dom.blur();

686 

687         this.isFocused = true;

688 

689         this.showPicker();

690     },

691 

692     destroy: function () {

693         this.callParent(arguments);

694         var store = this.getStore();

695 

696         if (store && store.getAutoDestroy()) {

697             Ext.destroy(store);

698         }

699 

700         Ext.destroy(this.listPanel, this.picker);

701     }

702 });

 

你可能感兴趣的:(Sencha Touch)