Я переупорядочиваю иерархию дерева с помощью перетаскивания. После перемещения нескольких узлов я получаю ошибку Uncaught RangeError: Maximum call stack size exceeded
. Ошибка появляется в NodeInterface.js
. Функция updateInfo
аварийно завершает работу в следующей строке
for (i = 0; i < childCount; i++) {
children[i].updateInfo(commit, childInfo);
}
Что могло вызвать эту проблему?
Следующий код показывает, как я реализовал перетаскивание и переупорядочение в ExtJS 6.5.2. Может быть, вы сможете найти причину проблемы.
Плагин
Ext.define('test.component.plugin.TreeDragger', {
extend: 'Ext.AbstractPlugin',
alias: 'plugin.treedrag',
mixins: ['Ext.mixin.Observable'],
constructor: function (config) {
this.mixins.observable.constructor.call(this, config);
},
init: function (component) {
var me = this;
this.source = new Ext.drag.Source({
element: component.element,
handle: '.x-gridrow',
constrain: {
element: true,
vertical: true
},
describe: function (info) {
var row = Ext.Component.from(info.eventTarget, component);
info.row = row;
info.record = row.getRecord();
},
proxy: {
type: 'placeholder',
getElement: function (info) {
console.info('proxy: getElement');
var el = Ext.getBody().createChild({
style: 'padding: 10px; width: 100px; border: 1px solid gray; color: red;',
});
el.show().update(info.record.get('description'));
return el;
}
},
// autoDestroy: false,
listeners: {
scope: me,
beforedragstart: me.makeRelayer('beforedragstart'),
dragstart: me.makeRelayer('dragstart'),
dragmove: me.makeRelayer('dragmove'),
dragend: me.makeRelayer('dragend')
}
});
},
disable: function () {
this.source.disable();
},
enable: function () {
this.source.enable();
},
doDestroy: function () {
Ext.destroy(this.source);
this.callParent();
},
makeRelayer: function (name) {
var me = this;
return function (source, info) {
return me.fireEvent(name, me, info);
};
}
});
Дерево
xtype: 'tree',
hideHeaders: true,
plugins: {
treedrag: {
type: 'treedrag',
listeners: {
beforedragstart: function (plugin, info) {
console.info('listeners: beforedragstart');
}
}
}
},
columns: [{
xtype: 'treecolumn',
flex: 1,
}
]
Контроллер
afterLoadApportionmentObjectsForTree: function (succes) {
if (succes) {
tree = this.getView().down('tree');
if (tree) {
tree.expandAll();
tree.updateHideHeaders(tree.getHideHeaders());
var store = tree.getStore();
store.remoteFilter = false;
store.filterer = 'bottomup';
this.createDropTargets();
}
}
},
createDropTargets: function () {
var me = this,
rows = tree.innerItems;
Ext.each(rows, function (el) {
var target = new Ext.drag.Target({
element: el.element,
listeners: {
scope: me,
drop: me.onDrop
}
});
});
},
onDrop: function (target, info, eOpts) {
var source = info.record,
row = Ext.Component.from(target.getElement(), tree),
destination = row.getRecord(),
parentNode = source.parentNode;
destination.appendChild(source);
destination.expand();
if (!parentNode.hasChildNodes()) {
parentNode.set('leaf', true);
}
}
Редактировать
Кажется, что updateInfo
называется рекурсивным, но я не могу понять, почему и как я могу это предотвратить.
Я мог сам найти ошибку. Было возможно перетаскивать узлы на их собственных дочерних элементов, что связано с рекурсивным добавлением и удалением одних и тех же узлов снова и снова. Чтобы пользователь не сделал этого, я добавил прослушиватель события beforeDrop
в свой Ext.drag.Target
. Там он возвращает false
, если целевой узел совпадает с исходным узлом, и возвращает false
, если целевой узел является дочерним узлом исходного узла.
onBeforeDrop: function (target, info, eOpts) {
var source = info.record,
row = Ext.Component.from(target.getElement(), tree),
destination = row.getRecord();
if (source == destination) {
return false;
}
if (source.findChild('number', destination.get('number'), true) != null) {
return false;
}
return true;
}
Я также использовал событие beforedragstart
, чтобы предотвратить перемещение корневого узла.
Может быть, это кому-то поможет.