Mar 20, 2019, 5:40 AM
[CLOSED] Bug in TreeStore
Support,
So this is a really nasty bug. Took a while to track it down and reproduce but the problem is that the Remove function isnt actually removing the Nodes from the TreeStore. It only fails in a specific case. The case is caused by a specific usage that causes the internal store to destabilize and then any Remove thereafter is broken.
to test:
step #1: run the example as is and notice the Messages and Archives exist.
step #2: only uncomment test #2 and you will see that Messages and Archives are properly removed.
step #3. comment test#2 out and uncomment test #3. notice that even though remove is called, Messages and Archives are STILL present. This is because the remove function on t3 fails internally destabilizing the store so any remove after that call is broken.
thanks,
/Z
So this is a really nasty bug. Took a while to track it down and reproduce but the problem is that the Remove function isnt actually removing the Nodes from the TreeStore. It only fails in a specific case. The case is caused by a specific usage that causes the internal store to destabilize and then any Remove thereafter is broken.
to test:
step #1: run the example as is and notice the Messages and Archives exist.
step #2: only uncomment test #2 and you will see that Messages and Archives are properly removed.
step #3. comment test#2 out and uncomment test #3. notice that even though remove is called, Messages and Archives are STILL present. This is because the remove function on t3 fails internally destabilizing the store so any remove after that call is broken.
thanks,
/Z
<%@ Page Language="C#" %>
<!DOCTYPE html>
<html>
<head runat="server">
<title>Multi Node Tree List built using markup - Ext.NET Examples</title>
<link href="/resources/css/examples.css" rel="stylesheet" />
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
if (!X.IsAjaxRequest)
{
//test #1
//run with both there
//Messages and Archives should be gone
var t1 = TreeStore1.GetNodeById("test1");
var t2 = TreeStore1.GetNodeById("test2");
//start test #2
/*
//run with both removed
//Messages and Archives should be gone
t1.Remove();
t2.Remove();
//end test #2
*/
//test #3
/*
//remove a "blank" that doesnt exist
//run with both removed
//Messages and Archives should be gone
//bug is that the blank seems to screw up the Treestore and after removing the "fake" blank, nothing is removed anymore. this is a critical issue that was
//causing menus to show for users that didnt have access to them. we temporarily fixed by excluding the blanks.
var t3 = TreeStore1.GetNodeById(""); //why does this return a values?? we expected NULL and then we null checked it. but it returns a valid value for something reason.
//js created by this removal screws up the tree for future removals
t3.Remove();
t1.Remove();
t2.Remove();
//end test #3
*/
}
}
</script>
<script type="text/javascript">
var syncSettings = function () {
App.ExpanderOnlyToggle.setChecked(App.TreeList1.getExpanderOnly());
App.SingleExpandToggle.setChecked(App.TreeList1.getSingleExpand());
App.MicroToggleBtn.setPressed(App.TreeList1.getMicro());
}
var handleSEToggle = function (me) {
if (me.checked) {
// If setting Single Expand, collapse everyone to ensure only one
// node ever gets expanded. Do not recurse into nodes, as manually
// collapsing a node does not collapse its children.
App.TreeList1.getStore().getRoot().collapseChildren(false);
}
App.TreeList1.setSingleExpand(me.checked);
}
var handleNavToggle = function (button, pressed) {
var treelist = App.TreeList1,
ct = treelist.ownerCt;
treelist.setExpanderFirst(!pressed);
treelist.setUi(pressed ? 'nav' : null);
treelist.setHighlightPath(pressed);
ct[pressed ? 'addCls' : 'removeCls']('treelist-with-nav');
// If switching to Nav while already on Micro, adjust the width
if (treelist.getMicro()) {
ct.setWidth(treelist.toolsElement.getWidth())
}
if (Ext.isIE8) {
this.repaintList(treelist);
}
}
var handleMicroToggle = function (me) {
var tl = App.TreeList1,
ct = tl.ownerCt;
App.TreeList1.setMicro(me.pressed);
if (me.pressed) {
tl.macroWidth = ct.getWidth();
ct.setWidth(tl.toolsElement.getWidth());
} else {
if (tl.macroWidth === undefined) {
tl.macroWidth = 200;
}
ct.setWidth(tl.macroWidth);
}
}
var handleTLSel = function (me, node) {
var pane = App.SelectionPanel,
cn = node,
selPath = [];
while (!cn.isRoot()) {
selPath.push(cn.data.text);
cn = cn.parentNode;
}
alert("Selected: <br /><b>" + selPath.reverse().join(" > ") + "</b>");
}
</script>
</head>
<body>
<form runat="server">
<ext:ResourceManager runat="server" Theme="Triton">
<Listeners>
<DocumentReady Fn="syncSettings" />
</Listeners>
</ext:ResourceManager>
<h1>Tree List Component Overview</h1>
<p>
The <b>TreeList</b> component is a simplified and lightweight version of the <b>TreePanel</b>, useful for simple and responsive navigation trees.
</p>
<ext:Window
ID="Window1"
runat="server"
Title="TreeList"
IconCls="x-fa fa-gears"
Width="500"
Height="450"
Closable="False"
Resizable="false"
Layout="BorderLayout">
<HeaderConfig runat="server">
<Items>
<ext:Button runat="server" Text="Options">
<Menu>
<ext:Menu runat="server" ID="OptionsMenu">
<Items>
<ext:CheckMenuItem
ID="ExpanderOnlyToggle"
runat="server"
Text="Expander Only"
Handler="App.TreeList1.setExpanderOnly(this.checked)" />
<ext:CheckMenuItem ID="SingleExpandToggle" runat="server" Text="Single Expand">
<Listeners>
<CheckChange Fn="handleSEToggle" />
</Listeners>
</ext:CheckMenuItem>
</Items>
</ext:Menu>
</Menu>
</ext:Button>
<ext:Button
ID="NavToggleBtn"
runat="server"
Text="Nav"
EnableToggle="true">
<Listeners>
<Toggle Fn="handleNavToggle" />
</Listeners>
</ext:Button>
<ext:Button
ID="MicroToggleBtn"
runat="server"
Text="Micro"
EnableToggle="true">
<Listeners>
<Toggle Fn="handleMicroToggle" />
</Listeners>
</ext:Button>
</Items>
</HeaderConfig>
<Items>
<ext:Panel
ID="TreeListPanel"
runat="server"
Width="250"
Border="false"
Region="West"
Split="true"
Scrollable="Both">
<LayoutConfig>
<ext:VBoxLayoutConfig Align="Stretch" />
</LayoutConfig>
<Items>
<ext:TreeList runat="server" ID="TreeList1">
<Listeners>
<SelectionChange Fn="handleTLSel" />
</Listeners>
<Store>
<ext:TreeStore ID="TreeStore1" runat="server">
<Root>
<ext:Node Expanded="true">
<Children>
<ext:Node Text="Home" IconCls="x-fa fa-home">
<Children>
<ext:Node NodeID="test1" Text="Messages" IconCls="x-fa fa-inbox" Leaf="true" />
<ext:Node NodeID="test2" Text="Archive" IconCls="x-fa fa-database">
<Children>
<ext:Node Text="First" IconCls="x-fa fa-sliders" Leaf="true" />
<ext:Node Text="No Icon" IconCls="" Leaf="true" />
</Children>
</ext:Node>
<ext:Node Text="Music" IconCls="x-fa fa-music" Leaf="true" />
<ext:Node Text="Video" IconCls="x-fa fa-film" Leaf="true" />
</Children>
</ext:Node>
<ext:Node Text="Users" IconCls="x-fa fa-user">
<Children>
<ext:Node Text="Tagged" IconCls="x-fa fa-tag" Leaf="true" />
<ext:Node Text="Inactive" IconCls="x-fa fa-trash" Leaf="true" />
</Children>
</ext:Node>
<ext:Node Text="Groups" IconCls="x-fa fa-group" Leaf="true" />
<ext:Node Text="Settings" IconCls="x-fa fa-wrench">
<Children>
<ext:Node Text="Sharing" IconCls="x-fa fa-share-alt" Leaf="true" />
<ext:Node Text="Notifications" IconCls="x-fa fa-flag" Leaf="true" />
<ext:Node Text="Network" IconCls="x-fa fa-signal" Leaf="true" />
</Children>
</ext:Node>
</Children>
</ext:Node>
</Root>
</ext:TreeStore>
</Store>
</ext:TreeList>
</Items>
</ext:Panel>
<ext:Panel
ID="SelectionPanel"
runat="server"
Region="Center"
BodyPadding="10" />
</Items>
</ext:Window>
</form>
</body>
</html>
Last edited by fabricio.murta; Apr 11, 2019 at 5:38 PM.