[FIXED] [#1307] [4.2.0] Control validation in ComponentColumn with TreePanel

  1. #1

    [FIXED] [#1307] [4.2.0] Control validation in ComponentColumn with TreePanel

    Hello,

    I have a TreePanel whose nodes are generated dynamically from code-behind. There is a ComponentColumn which contains a NumberField. Based on a specific condition a NumberField is generated or not (in the code example below for simplicity's sake, I take the node name as my condition). I also want to disable the NumberField if the node is not checked, and enable it when the node is checked.

    The problem, however, is that the NumberField is being rendered over and over, with the ID constantly changing. So when I try to get a value or validate, it seems like an old control is being fetched instead of the current one.

    I have provided a sample of the problem. Check one of the nodes and then click on the Save button.

    <!DOCTYPE html>
    
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title></title>
        <script>
            var beforeBindWeightField = function (componentColumn, context) {
                //random condition to bind or not bind
                return (context.record.data.text == "Test");
            }
    
            var bindWeightField = function (componentColumn, field, node) {
                field.setDisabled(!node.data.checked);
            }
    
            function validateForm() {
                var valid = true;
                var column = App.treeParameters.headerCt.items.get(1);
                var len = App.treeParameters.getStore().getCount();
                var field;
    
                for (var i = 0; i < len; i++) {
                    field = column.getComponent(i);
                    if (field != null) {
                        if (!field.isValid() || (field.disabled == false && field.value == null)) {
                            valid = false;
                            break;
                        }
                    }
    
                    alert(valid);
                }
            }
        </script>
        <script runat="server">
            protected void Page_Load(object sender, EventArgs e)
            {
                try
                {
                    if (!IsPostBack)
                    {
                        treeParameters.Root.Add(new Node() { Checked = false });
                        Node.Config nodeConfig = new Node.Config();
                        nodeConfig.Checked = false;
                        nodeConfig.Leaf = true;
                        NodeProxy rootnode = treeParameters.GetRootNode();
    
                        for (int i = 0; i < 5; i++)
                        {
                            Node node = new Node();
                            node.Text = (i % 2 == 0 ? "Test" : "test");
                            node.Expanded = true;
                            node.Checked = false;
                            node.Leaf = false;
                            node.Icon = Icon.GroupGear;
                            node.NodeID = "Test" + i;
                            rootnode.AppendChild(node);
                        }
                    }
                }
                catch (Exception ex)
                {
    
                }
            }
        </script>
    </head>
    <body>
        <form id="form1" runat="server">
            <ext:ResourceManager runat="server"></ext:ResourceManager>
            <ext:TreePanel Title="Configuration Parameters" runat="server" ID="treeParameters" RootVisible="false" Border="true"
                Height="400" Lines="false" UseArrows="true" DisableSelection="true">
                <Store>
                    <ext:TreeStore ID="treeStoreParemeters" runat="server" ShowWarningOnFailure="false">
                    </ext:TreeStore>
                </Store>
                <ColumnModel>
                    <Columns>
                        <ext:TreeColumn runat="server" DataIndex="text" Width="400" Text="Parameter" />
                        <ext:ComponentColumn ID="clComponent" runat="server" Width="120" Text="Weight">
                            <Component>
                                <ext:NumberField runat="server" AllowBlank="false" MinValue="0">
                                </ext:NumberField>
                            </Component>
                            <Listeners>
                                <BeforeBind Fn="beforeBindWeightField" />
                                <Bind Fn="bindWeightField" />
                            </Listeners>
                        </ext:ComponentColumn>
                    </Columns>
                </ColumnModel>
            </ext:TreePanel>
            <ext:Button runat="server" ID="btnSave" Text="Save">
                <Listeners>
                    <Click Handler="validateForm();" />
                </Listeners>
            </ext:Button>
        </form>
    </body>
    </html>
    Last edited by Daniil; May 12, 2016 at 12:14 PM.
  2. #2
    Hello @FpNetWorth!

    You found a bug! The componentColumn is keeping cached the old components every time it creates new replacing components.

    Actually, this is not clear to me still why are the components being recreated over and over, and that should be investigated as well!

    For now, add this to your JavaScript code. It is a little cumbersome, but as a workaround it should do!

            Ext.define('Ext.grid.column.ComponentColumn', {
                override: 'Ext.grid.column.ComponentColumn',
                insertComponentForRecord: function (record, node, refreshSize) {
                    var me = this,
                        cachedRecords = [],
                        i, j, entry, isDup;
    
                    this.callParent(arguments);
    
                    for (i in this.cache) {
                        entry = this.cache[i];
    
                        isDup = false;
                        for (j in cachedRecords) {
                            if (entry.id == cachedRecords[j]) {
                                isDup = true;
                                this.removeComponent(null, { id: entry.id });
                            }
                        }
    
                        if (!isDup) {
                            cachedRecords.push(entry.id); // adds to the list of already present
                        }
                    }
                }
            });
    Let us know if this works for you. Your assumption of IDs frequently changing is true and it becomes clear if I change your example like this:
    <%@ Page Language="C#" %>
    
    <!DOCTYPE html>
    
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title></title>
        <script>
            var beforeBindWeightField = function (componentColumn, context) {
                //random condition to bind or not bind
                return (context.record.data.text == "Test");
            }
    
            var bindWeightField = function (componentColumn, field, node) {
                if (node.data.checked) {
                    field.enable();
                } else {
                    field.disable();
                }
            }
    
            function validateForm() {
                var valid = true;
                var column = App.treeParameters.headerCt.items.get(1);
                var len = App.treeParameters.getStore().getCount();
                var field;
                var message = "Status: <br />";
                var msgAppend;
    
                for (var i = 0; i < len; i++) {
                    field = column.getComponent(i);
                    msgAppend = "";
                    if (field != null) {
                        msgAppend = ", iVd:" + field.isValid() + ", iD:" + field.disabled + ", fVl:" + field.value +
                            ", id:" + field.id;
                        if (!field.isValid() || (field.disabled == false && field.value == null)) {
                            valid = false;
    
                            // check the other rows, don't break here for now!
                            //break;
                        }
                    }
    
                    message += i + "[" + valid + msgAppend + "]<br />";
                }
                App.StatusBar1.setStatus(message)
            }
    
            Ext.define('Ext.grid.column.ComponentColumn', {
                override: 'Ext.grid.column.ComponentColumn',
                insertComponentForRecord: function (record, node, refreshSize) {
                    var me = this,
                        cachedRecords = [],
                        i, j, entry, isDup;
    
                    this.callParent(arguments);
    
                    for (i in this.cache) {
                        entry = this.cache[i];
    
                        isDup = false;
                        for (j in cachedRecords) {
                            if (entry.id == cachedRecords[j]) {
                                isDup = true;
                                this.removeComponent(null, { id: entry.id });
                            }
                        }
    
                        if (!isDup) {
                            cachedRecords.push(entry.id); // adds to the list of already present
                        }
                    }
                }
            });
        </script>
        <script runat="server">
            protected void Page_Load(object sender, EventArgs e)
            {
                try
                {
                    if (!IsPostBack)
                    {
                        treeParameters.Root.Add(new Node() { Checked = false });
                        Node.Config nodeConfig = new Node.Config();
                        nodeConfig.Checked = false;
                        nodeConfig.Leaf = true;
                        NodeProxy rootnode = treeParameters.GetRootNode();
    
                        for (int i = 0; i < 5; i++)
                        {
                            Node node = new Node();
                            node.Text = (i % 2 == 0 ? "Test" : "test");
                            node.Expanded = true;
                            node.Checked = false;
                            node.Leaf = false;
                            node.Icon = Icon.GroupGear;
                            node.NodeID = "Test" + i;
                            rootnode.AppendChild(node);
                        }
                    }
                }
                catch (Exception ex)
                {
    
                }
            }
        </script>
    </head>
    <body>
        <form id="form1" runat="server">
            <ext:ResourceManager runat="server"></ext:ResourceManager>
            <ext:TreePanel Title="Configuration Parameters" runat="server" ID="treeParameters" RootVisible="false" Border="true"
                Height="400" Lines="false" UseArrows="true" DisableSelection="true">
                <Store>
                    <ext:TreeStore ID="treeStoreParemeters" runat="server" ShowWarningOnFailure="false">
                    </ext:TreeStore>
                </Store>
                <ColumnModel>
                    <Columns>
                        <ext:TreeColumn runat="server" DataIndex="text" Width="400" Text="Parameter" />
                        <ext:ComponentColumn ID="clComponent" runat="server" Width="120" Text="Weight">
                            <Component>
                                <ext:NumberField runat="server" AllowBlank="false" MinValue="0">
                                </ext:NumberField>
                            </Component>
                            <Listeners>
                                <BeforeBind Fn="beforeBindWeightField" />
                                <Bind Fn="bindWeightField" />
                            </Listeners>
                        </ext:ComponentColumn>
                    </Columns>
                </ColumnModel>
            </ext:TreePanel>
            <ext:Button runat="server" ID="btnSave" Text="Save">
                <Listeners>
                    <Click Handler="validateForm();" />
                </Listeners>
            </ext:Button>
            <ext:StatusBar runat="server" ID="StatusBar1" />
        </form>
    </body>
    </html>
    Every time you click 'save' you have the results output below the grid, and you can see how the IDs change.

    I believe the component was made to be rebuilt every time to avoid some problem, so maybe reverting it not to recreate every time might become a problem!

    We have logged this as issue #1307, we'll update it here as soon as we have fixed in code! Meanwhile we'd appreciate your feedback on the override provided above!

    Hope this helps!
    Fabrício Murta
    Developer & Support Expert
  3. #3
    Hello @fabricio.murta!

    I can confirm that this workaround is working well.

    As for the component being rebuilt every time, for me there is no need for it to be reverted. It works as is. My main concern was submitting the data on save, and I can successfully do that.

    Thank you for the workaround!
  4. #4
    Thanks for the confirmation, we'll be fixing this on our version and we'll update here once it is done! Thanks also for the report, we really appreciate it!
    Fabrício Murta
    Developer & Support Expert
  5. #5
    The bug has been fixed in both v3 and v4 sources. It will be included into 4.2.0 release. And to the next 3.x release if ever.

    As for recreating components every time (Issue #1308), then yes, it is by design as it is the only solid way to cover many possible scenarios. Each time GridPanel re-renders (recreates) a row, we recreate a ComponentColumn's components. Although, we might be able to come up with some solution in the future not to recreate always and optimize the process in some way. Maybe. Anyways, no time frame for that.

Similar Threads

  1. Replies: 12
    Last Post: Jul 28, 2015, 11:11 AM
  2. [OPEN] [#234] ComponentColumn Error
    By bayoglu in forum 2.x Premium Help
    Replies: 12
    Last Post: May 08, 2013, 11:28 AM
  3. [CLOSED] [#36] ComponentColumn + treePanel
    By aisi_it_admin in forum 2.x Premium Help
    Replies: 4
    Last Post: Feb 14, 2013, 10:33 AM
  4. [OPEN] [#100] Validation - Remote/Client
    By adelaney in forum 2.x Premium Help
    Replies: 7
    Last Post: Dec 29, 2012, 7:12 AM
  5. Replies: 3
    Last Post: Jul 11, 2011, 10:43 AM

Posting Permissions