extjs7 store重新加载导致异常
小编:管理员 532阅读 2022.09.06
7.4.0 classic
现象grid/treegrid使用actioncolumn或其他能获得焦点的单元格元素交互后,刷新store,如果操作的目标行不在新数据中(例如actioncolumn中按钮执行删除操作,异步提交后刷新store),将报错如下
解决Uncaught TypeError: Cannot read properties of null (reading ‘focus’) at constructor.setActionableMode (Table.js?_dc=1640829487431:4262) at constructor.setActionableMode (Table.js?_dc=1640829487431:2744) at constructor.onFocusLeave (Table.js?_dc=1640829487431:2438) at constructor.onGlobalFocus (ComponentManager.js?_dc=1640829487428:315) at constructor.fire (Event.js?_dc=1640829487428:523) at constructor.doFireEvent (Observable.js?_dc=1640829487428:884) at constructor.prototype.doFireEvent (EventDomain.js?_dc=1640829487428:302) at constructor.fireEventArgs (Observable.js?_dc=1640829487428:717) at constructor.fireEvent (Observable.js?_dc=1640829487428:664) at constructor.processFocusIn (Focus.js?_dc=1640829487430:121)
删除操作提交成功后,使用store.remove(recordRemoved)将已删除数据从store中移出,如有需要(远端分页查询场景)在执行store.load()
源码分析- load后会根据此前焦点的行记录重新定位焦点 但是记录已经不存在,源码没有重新校验导致定位焦点异常 ext-classic/src/view/Table.js
/** * * @param {Boolean} enabled * @param {Ext.grid.CellContext} position The cell to activate. * @param {HTMLElement/Ext.dom.Element} [position.target] The element within the referenced * cell to focus. * @return {Boolean} Returns `false` if the mode did not change. * @private */ setActionableMode: function(enabled, position) { var me = this, navModel = me.getNavigationModel(), actionables = me.grid.actionables, len = actionables.length, isActionable = false, activeEl, record, column, lockingPartner, cell, i; // No mode change. // ownerGrid's call will NOT fire mode change event upon false return. if (me.actionableMode === enabled) { // If we're not actionable already, or (we are actionable already at that position) // return false. // Test using mandatory passed position because we may not have an actionPosition // if we are the lockingPartner of an actionable view that contained // the action position. // // If we being told to go into actionable mode but at another position, // we must continue. This is just actionable navigation. if (!enabled || position.isEqual(me.actionPosition)) { return false; } } // If this View or its lockingPartner contains the current focus position, // then make the tab bumpers tabbable and move them to surround the focused row. if (enabled) { if (position && (position.view === me || (position.view === (lockingPartner = me.lockingPartner) && lockingPartner.actionableMode))) { isActionable = me.activateCell(position); } // Did not enter actionable mode. // ownerGrid's call will NOT fire mode change event upon false return. return isActionable; } else { // Capture before exiting from actionable mode moves focus activeEl = Ext.fly(Ext.Element.getActiveElement()); // Blur the focused descendant, but do not trigger focusLeave. // This is so that when the focus is restored to the cell which contained // the active content, it will not be a FocusEnter from the universe. if (me.el.contains(activeEl) && !Ext.fly(activeEl).is(me.getCellSelector())) { // Row to return focus to. // 此处会获取到此前操作焦点的行记录 record = (me.actionPosition && me.actionPosition.record) || me.getRecord(activeEl); column = me.getHeaderByCell(activeEl.findParent(me.getCellSelector())); cell = position && position.getCell(true); // Do not allow focus to fly out of the view when the actionables // are deactivated (and blurred/hidden). Restore focus to the cell in which // actionable mode is active. // Note that the original position may no longer be valid, e.g. when the record // was removed. if (!position || !cell) { // 重新根据record获取position和cell后没有校验,此时position.rowIdx=-1,cell=flase position = new Ext.grid.CellContext(me).setPosition(record || 0, column || 0); cell = position.getCell(true); } // Ext.grid.NavigationModel#onFocusMove will NOT react and navigate // because the actionableMode flag is still set at this point. Ext.fly(cell).focus(); // Let's update the activeEl after focus here activeEl = Ext.fly(Ext.Element.getActiveElement()); // If that focus triggered handlers (eg CellEditor after edit handlers) which // programmatically moved focus somewhere, and the target cell has been // unfocused, defer to that, null out position, so that we do not navigate // to that cell below. // See EXTJS-20395 if (!(me.el.contains(activeEl) && activeEl.is(me.getCellSelector()))) { position = null; } } // We are exiting actionable mode. // Tell all registered Actionables about this fact if they need to know. for (i = 0; i < len; i++) { if (actionables[i].deactivate) { actionables[i].deactivate(); } } // If we had begun action (we may be a dormant lockingPartner), // make any tabbables untabbable if (me.actionRow) { me.actionRow.saveTabbableState({ skipSelf: true, includeSaved: false }); } if (me.destroyed) { return false; } // These flags MUST be set before focus restoration to the owning cell. // so that when Ext.grid.NavigationModel#setPosition attempts to exit // actionable mode, we don't recurse. me.actionableMode = me.ownerGrid.actionableMode = false; me.actionPosition = navModel.actionPosition = me.actionRow = null; // Push focus out to where it was requested to go. if (position) { navModel.setPosition(position); } } },复制
- 执行remove操作后,会自动释放焦点 此后在执行load操作则不会报错。 ext-classic/src/view/AbstractView.js
onRemove: function(store, records, index) { var me = this, rows = me.all, currIdx, i, record, nodes, node, restoreFocus; if (me.rendered && !me.refreshNeeded && rows.getCount()) { if (me.dataSource.getCount() === 0) { me.refresh(); } else { // If this view contains focus, this will return // a function which will restore that state. restoreFocus = me.saveFocusState(); // Just remove the elements which corresponds to the removed records // The tpl's full HTML will still be in place. nodes = []; for (i = records.length - 1; i >= 0; --i) { record = records[i]; currIdx = index + i; if (nodes) { node = rows.item(currIdx); nodes[i] = node ? node.dom : undefined; } if (rows.item(currIdx)) { me.doRemove(record, currIdx); } } me.fireItemMutationEvent('itemremove', records, index, nodes, me); // If focus was in this view, this will restore it // 此处释放焦点 restoreFocus(); me.updateIndexes(index); } // Ensure layout system knows about new content size me.refreshSizePending = true; } },复制
相关推荐
- ExtJs七(ExtJs Mvc创建ViewPort) 前言在4.1的时候,要先创建一个扩展于Ext.app.Application的类,然后用create创建它的实例来开始应用程序的。而在4.1.1,则可直接调用application方法开始执行应用程序,简化了。调用application方法,其参数是一个配置对象,主要配置项有以下三个:name:用来…
- 3DMAX提示和技巧 本主题标识使用 Civil View 的一些重要提示和技巧。常规使用屏幕分辨率至少为 1280x1024 的 Civil View。低于此分辨率时,一些面板将占用过多屏幕空间。 将视口设置为线框显示以达到最佳性能。 要尽可能简化用户界面,请在单个视口中工作并关闭 3ds Max 命令面…