Success! Looks like we've fixed this one. According to our records the fix was applied for EXTJS-9804 in a recent build.
  1. #1
    Sencha Premium Member
    Join Date
    Dec 2011
    Location
    London, UK
    Posts
    260
    Vote Rating
    8
    bseddon will become famous soon enough

      0  

    Default Regression in 4.2.1 846

    Regression in 4.2.1 846


    There is a regression introduced sometime after 778. I think it is related to the fix for EXTJSIV-9658 (http://www.sencha.com/forum/showthre...ression-in-4.2). Or at least this issue appears with or after this this fix.

    We have a need to show a tree grid column if, and only if, there are values in that column. We effect this by reviewing the column values (values in the related store) to determine if the column should be shown. If it should be shown, the container's header ct is retrieved and the column's visibility set appropriately. In fact we compute a boolean by iterating over the nodes in the tree store. The column's visiblity is set by assigning the value of the computed boolean.

    This may or may not be an appropriate means to achieve our ends but in versions including at least 778 this worked. In 846 technique not only stopped working it hangs the application because setting the column's visibility triggers a recursion: calling setVisibility in 'afterLayout' event prompts another layout and so on.

    The example illustrates the issue.

    The work around is simple: look at the column's visibility and only change it if the circumstances warrant it.

    However it is a regression. Presumably earlier version of ExtJS performed this check.

    Code:
    Ext.define('MyApp.model.Formula', {
        extend: 'Ext.data.Model',
    
        fields: [
            {
                name: 'partName',
                sortType: 'asUCText',
                type: 'string'
            },
            {
                name: 'partValue',
                sortType: 'asUCText',
                type: 'string'
            },
            {
                name: 'formulaPartType',
                sortType: 'asUCText',
                type: 'string'
            },
            {
                name: 'resultType',
                sortType: 'asUCText',
                type: 'string'
            },
            {
                name: 'operatorType',
                sortType: 'asUCText',
                type: 'string'
            },
            {
                name: 'operatorClass',
                sortType: 'asUCText',
                type: 'string'
            },
            {
                name: 'assignments',
                sortType: 'asUCText',
                type: 'string'
            },
            {
                name: 'lineNumber',
                sortType: 'asInt',
                type: 'int'
            },
            {
                name: 'warning',
                sortType: 'asUCText',
                type: 'string'
            },
            {
                name: 'issue',
                sortType: 'asInt',
                type: 'int'
            }
        ]
    });
    
    Ext.define('MyApp.store.Formulas', {
        extend: 'Ext.data.TreeStore',
    
        requires: [
            'MyApp.model.Formula'
        ],
    
        constructor: function(cfg) {
            var me = this;
            cfg = cfg || {};
            me.callParent([Ext.apply({
                model: 'MyApp.model.Formula',
                storeId: 'Formulas',
                root: {
                    lineNumber: -6,
                    resultType: 'Any',
                    formulaPartType: 'Root',
                    partName: 'Root',
                    partValue: '',
                    leaf: false,
                    expanded: true,
                    children: [
                        {
                            lineNumber: -5,
                            resultType: 'Any',
                            formulaPartType: 'Scope',
                            partName: 'Scope',
                            partValue: 'b156',
                            leaf: true
                        },
                        {
                            lineNumber: -1,
                            resultType: 'Number',
                            formulaPartType: 'Number',
                            partName: 'Result',
                            partValue: '16',
                            leaf: false,
                            isdefault: true,
                            assignments: [
                                {
                                    author: 'administrator',
                                    creationdate: '2013-03-17T23:19:34',
                                    name: 'Number',
                                    sourcenodepath: '/root/any/number',
                                    array: false,
                                    isdefault: true
                                }
                            ],
                            formula: '=IF(AND(B146>1.2, B136<>"hello"), SUM(B138:B140,B141)+87-B143, AVERAGE(B21:B32)) +( B141+ForB156)',
                            expanded: true,
                            children: [
                                {
                                    lineNumber: 0,
                                    resultType: 'Any',
                                    formulaPartType: 'Function',
                                    partName: 'IF',
                                    partValue: '6',
                                    leaf: false,
                                    isdefault: true,
                                    assignments: [
                                        {
                                            author: 'administrator',
                                            creationdate: '2013-03-17T23:19:37',
                                            name: 'Any',
                                            sourcenodepath: '/root/any',
                                            array: false,
                                            isdefault: false
                                        }
                                    ],
                                    formula: 'IF(AND(B146>1.2,B136<>"hello"),SUM(B138:B140,B141)+87-B143,AVERAGE(B21:B32))',
                                    expanded: true,
                                    children: [
                                        {
                                            lineNumber: -2,
                                            resultType: 'Any',
                                            formulaPartType: 'Function',
                                            partName: 'Condition',
                                            partValue: 'FALSE',
                                            leaf: false,
                                            formula: 'Condition',
                                            expanded: true,
                                            children: [
                                                {
                                                    lineNumber: 1,
                                                    resultType: 'Boolean',
                                                    formulaPartType: 'Function',
                                                    partName: 'AND',
                                                    partValue: 'FALSE',
                                                    leaf: false,
                                                    isdefault: true,
                                                    internalFixed: true,
                                                    assignments: [
                                                        {
                                                            author: 'administrator',
                                                            creationdate: '2013-03-17T23:19:37',
                                                            name: 'Boolean',
                                                            sourcenodepath: '/root/any/boolean',
                                                            array: false,
                                                            isdefault: false
                                                        }
                                                    ],
                                                    formula: 'AND(B146>1.2,B136<>"hello")',
                                                    expanded: true,
                                                    children: [
                                                        {
                                                            lineNumber: 2,
                                                            resultType: 'Any',
                                                            formulaPartType: 'Reference',
                                                            referenceClass: 'Cell',
                                                            partName: 'B146',
                                                            partValue: '0.5',
                                                            leaf: true
                                                        },
                                                        {
                                                            lineNumber: 3,
                                                            resultType: 'Number',
                                                            formulaPartType: 'Operator',
                                                            operatorType: 'Gt',
                                                            operatorClass: 'Boolean',
                                                            partName: '>',
                                                            partValue: '',
                                                            leaf: true
                                                        },
                                                        {
                                                            lineNumber: 4,
                                                            resultType: 'Number',
                                                            formulaPartType: 'Number',
                                                            partName: '1.2',
                                                            partValue: '1.2',
                                                            leaf: true
                                                        },
                                                        {
                                                            lineNumber: 5,
                                                            resultType: 'Number',
                                                            formulaPartType: 'Operator',
                                                            operatorType: 'Separator',
                                                            operatorClass: 'Separator',
                                                            partName: ',',
                                                            partValue: '',
                                                            leaf: true
                                                        },
                                                        {
                                                            lineNumber: 6,
                                                            resultType: 'Any',
                                                            formulaPartType: 'Reference',
                                                            referenceClass: 'Cell',
                                                            partName: 'B136',
                                                            partValue: '"hello"',
                                                            leaf: true
                                                        },
                                                        {
                                                            lineNumber: 7,
                                                            resultType: 'Number',
                                                            formulaPartType: 'Operator',
                                                            partName: '<>',
                                                            partValue: '',
                                                            leaf: true
                                                        },
                                                        {
                                                            lineNumber: 8,
                                                            resultType: 'String',
                                                            formulaPartType: 'String',
                                                            partName: '"hello"',
                                                            partValue: '"hello"',
                                                            leaf: true
                                                        }
                                                    ]
                                                }
                                            ]
                                        },
                                        {
                                            lineNumber: -3,
                                            resultType: 'Any',
                                            formulaPartType: 'Function',
                                            partName: 'True',
                                            partValue: '82.6',
                                            leaf: false,
                                            formula: 'True',
                                            expanded: true,
                                            children: [
                                                {
                                                    lineNumber: 10,
                                                    resultType: 'Number',
                                                    formulaPartType: 'Function',
                                                    partName: 'SUM',
                                                    partValue: '23',
                                                    leaf: false,
                                                    isdefault: true,
                                                    internalFixed: true,
                                                    assignments: [
                                                        {
                                                            author: 'administrator',
                                                            creationdate: '2013-03-17T23:19:37',
                                                            name: 'Number',
                                                            sourcenodepath: '/root/any/number',
                                                            array: false,
                                                            isdefault: false
                                                        }
                                                    ],
                                                    formula: 'SUM(B138:B140,B141)',
                                                    expanded: true,
                                                    children: [
                                                        {
                                                            lineNumber: 11,
                                                            resultType: 'Any',
                                                            formulaPartType: 'Reference',
                                                            referenceClass: 'Area',
                                                            partName: 'B138:B140',
                                                            partValue: '$B$138:$B$140',
                                                            leaf: true
                                                        },
                                                        {
                                                            lineNumber: 12,
                                                            resultType: 'Number',
                                                            formulaPartType: 'Operator',
                                                            operatorType: 'Separator',
                                                            operatorClass: 'Separator',
                                                            partName: ',',
                                                            partValue: '',
                                                            leaf: true
                                                        },
                                                        {
                                                            lineNumber: 13,
                                                            resultType: 'Any',
                                                            formulaPartType: 'Reference',
                                                            referenceClass: 'Cell',
                                                            partName: 'B141',
                                                            partValue: '2',
                                                            leaf: true
                                                        }
                                                    ]
                                                },
                                                {
                                                    lineNumber: 14,
                                                    resultType: 'Number',
                                                    formulaPartType: 'Operator',
                                                    operatorType: 'Plus',
                                                    operatorClass: 'Arithmetic',
                                                    partName: '+',
                                                    partValue: '',
                                                    leaf: true
                                                },
                                                {
                                                    lineNumber: 15,
                                                    resultType: 'Number',
                                                    formulaPartType: 'Number',
                                                    partName: '87',
                                                    partValue: '87',
                                                    leaf: true
                                                },
                                                {
                                                    lineNumber: 16,
                                                    resultType: 'Number',
                                                    formulaPartType: 'Operator',
                                                    operatorType: 'Minus',
                                                    operatorClass: 'Arithmetic',
                                                    partName: '-',
                                                    partValue: '',
                                                    leaf: true
                                                },
                                                {
                                                    lineNumber: 17,
                                                    resultType: 'Any',
                                                    formulaPartType: 'Reference',
                                                    referenceClass: 'Cell',
                                                    partName: 'B143',
                                                    partValue: '27.4',
                                                    leaf: true
                                                }
                                            ]
                                        },
                                        {
                                            lineNumber: -4,
                                            resultType: 'Any',
                                            formulaPartType: 'Function',
                                            partName: 'False',
                                            partValue: '6',
                                            leaf: false,
                                            formula: 'False',
                                            expanded: true,
                                            children: [
                                                {
                                                    lineNumber: 19,
                                                    resultType: 'Number',
                                                    formulaPartType: 'Function',
                                                    partName: 'AVERAGE',
                                                    partValue: '6',
                                                    leaf: false,
                                                    isdefault: true,
                                                    internalFixed: true,
                                                    assignments: [
                                                        {
                                                            author: 'administrator',
                                                            creationdate: '2013-03-17T23:19:37',
                                                            name: 'Number',
                                                            sourcenodepath: '/root/any/number',
                                                            array: false,
                                                            isdefault: false
                                                        }
                                                    ],
                                                    formula: 'AVERAGE(B21:B32)',
                                                    expanded: true,
                                                    children: [
                                                        {
                                                            lineNumber: 20,
                                                            resultType: 'Any',
                                                            formulaPartType: 'Reference',
                                                            referenceClass: 'Area',
                                                            partName: 'B21:B32',
                                                            partValue: '$B$21:$B$32',
                                                            leaf: true
                                                        }
                                                    ]
                                                }
                                            ]
                                        }
                                    ]
                                },
                                {
                                    lineNumber: 21,
                                    resultType: 'Number',
                                    formulaPartType: 'Operator',
                                    operatorType: 'Plus',
                                    operatorClass: 'Arithmetic',
                                    partName: '+',
                                    partValue: '',
                                    leaf: true
                                },
                                {
                                    lineNumber: 22,
                                    resultType: 'Number',
                                    formulaPartType: 'Function',
                                    partName: '',
                                    partValue: '10',
                                    leaf: false,
                                    isdefault: true,
                                    assignments: [
                                        {
                                            author: 'administrator',
                                            creationdate: '2013-03-17T23:19:34',
                                            name: 'Number',
                                            sourcenodepath: '/root/any/number',
                                            array: false,
                                            isdefault: true
                                        }
                                    ],
                                    formula: '(B141+ForB156)',
                                    expanded: true,
                                    children: [
                                        {
                                            lineNumber: 23,
                                            resultType: 'Any',
                                            formulaPartType: 'Reference',
                                            referenceClass: 'Cell',
                                            partName: 'B141',
                                            partValue: '2',
                                            leaf: true
                                        },
                                        {
                                            lineNumber: 24,
                                            resultType: 'Number',
                                            formulaPartType: 'Operator',
                                            operatorType: 'Plus',
                                            operatorClass: 'Arithmetic',
                                            partName: '+',
                                            partValue: '',
                                            leaf: true
                                        },
                                        {
                                            lineNumber: 25,
                                            resultType: 'Number',
                                            formulaPartType: 'Name',
                                            partName: 'ForB156',
                                            partValue: '8',
                                            leaf: true
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                },
                proxy: {
                    type: 'memory',
                    reader: {
                        type: 'json'
                    }
                }
            }, cfg)]);
        }
    });
    
    Ext.define('MyApp.view.MyViewport', {
        extend: 'Ext.container.Viewport',
    
        layout: {
            type: 'border'
        },
    
        initComponent: function() {
            var me = this;
    
            Ext.applyIf(me, {
                items: [
                    {
                        xtype: 'treepanel',
                        region: 'center',
                        id: 'formulagrid',
                        ui: 'orange-panel',
                        title: 'Formula Grid',
                        columnLines: true,
                        store: 'Formulas',
                        rootVisible: false,
                        viewConfig: {
                            id: 'formulaGridView'
                        },
                        columns: [
                            {
                                xtype: 'treecolumn',
                                width: 140,
                                dataIndex: 'partName',
                                text: 'Part Name'
                            },
                            {
                                xtype: 'gridcolumn',
                                dataIndex: 'partValue',
                                text: 'Value',
                                flex: 1
                            },
                            {
                                xtype: 'gridcolumn',
                                renderer: function(value, metaData, record, rowIndex, colIndex, store, view) {
                                    var data = record.data;
    
                                    return data.operatorClass === "Separator" ? data.operatorClass : value;
                                },
                                hidden: true,
                                dataIndex: 'formulaPartType',
                                text: 'Part Type'
                            },
                            {
                                xtype: 'gridcolumn',
                                hidden: true,
                                dataIndex: 'resultType',
                                text: 'Result Type'
                            },
                            {
                                xtype: 'gridcolumn',
                                hidden: true,
                                dataIndex: 'operatorClass',
                                text: 'Part Class'
                            },
                            {
                                xtype: 'gridcolumn',
                                renderer: function(value, metaData, record, rowIndex, colIndex, store, view) {
                                    try
                                    {
                                        if (record.data.lineNumber === -1 /* 'Result' */ || record.data.lineNumber === -5 /* 'Scope' */)
                                        {
                                            return (record.data.assignments === undefined ||
                                            record.data.assignments.length === 0)
                                            ? "Any"
                                            : record.getAssignmentNames();
                                        }
                                        else if (record.data.formulaPartType == 'Function')
                                        {
                                            if (record.data.lineNumber >= 0)
                                            if (record.data.internalFixed)
                                            return record.getAssignmentNames();
                                            else if (record.data.assignments === undefined ||
                                            record.data.assignments.length === 0)
                                            return "Any";
                                            else
                                            return record.getAssignmentNames();
                                        }
                                        else if (record.data.formulaPartType == 'Reference' &&
                                        record.data.assignments !== undefined &&
                                        record.data.assignments.length > 0)
                                        return record.getAssignmentNames();
                                    }
                                    catch(ex)
                                    {}
    
                                },
                                dataIndex: 'assignments',
                                text: 'Assignments'
                            },
                            {
                                xtype: 'gridcolumn',
                                renderer: function(value, metaData, record, rowIndex, colIndex, store, view) {
                                    var scopeAssigned = "images/formula/ScopeAssigned.png";
                                    var scopeUnassigned = "images/formula/ScopeUnassigned.png";
                                    var typeAssigned = "images/formula/TypeAssigned.png";
                                    var typeUnassigned = "images/formula/TypeUnassigned.png";
                                    var typeDefault = "images/formula/TypeUnassigned.png";
                                    var typeFixed = "images/formula/TypeFixed.png";
                                    var ruleUnassigned = "images/formula/RuleUnassigned.png";
                                    var ruleAssigned = "images/formula/RuleAssigned.png";
    
                                    var title = "";
                                    var src = "";
                                    var assignments = "";
                                    try
                                    {
                                        assignments = record.getAssignmentNames().toString();
                                    }
                                    catch(ex) {}
    
                                    if (record.data.lineNumber === -5 /*'Scope'*/ )
                                    {
                                        if (record.data.assignments === undefined ||
                                        record.data.assignments.length === 0)
                                        {
                                            src=scopeUnassigned;
                                            title = "Any scope is valid";
                                        }
                                        else
                                        {
                                            src=scopeAssigned;
                                            title = "Cell scope(s) assigned ({0})";        
                                        }
    
                                    }
                                    else if (record.data.lineNumber === -1 /*'Result'*/ )
                                    {
                                        if (record.data.isdefault)
                                        {
                                            src=typeDefault;
                                            title = "Default result type(s) assigned ({0})";
                                        }
                                        else
                                        if (record.data.assignments === undefined ||
                                        record.data.assignments.length === 0)
                                        {
                                            src=typeUnassigned;
                                            title = "Any result type is valid";
                                        }
                                        else
                                        {
                                            src=typeAssigned;
                                            title = "Result type(s) assigned ({0})";        
                                        }
    
                                    }
                                    else if (record.data.formulaPartType === 'Function' || record.data.formulaPartType === 'Name')
                                    {
                                        if (record.data.partName === "") return "";
    
                                        if (record.data.lineNumber >= 0)
                                        {
                                            if (record.data.internalFixed)
                                            {
                                                src=typeFixed;
                                                title="Type fixed ({0})";
                                            }
                                            else if (record.data.assignments === undefined || record.data.assignments.length === 0)
                                            {
                                                src=typeUnassigned;
                                                title="Any result type valid";
                                            }
                                            else
                                            {
                                                src=typeAssigned;
                                                title="Result type(s) assigned ({0})";
                                            }
                                        }
                                    }
                                    else if (record.data.formulaPartType == 'Reference')
                                    {
                                        if (record.data.assignments === undefined || record.data.assignments.length === 0)
                                        {
                                            src=ruleUnassigned;
                                            title="No validation rule assigned";
                                        }
                                        else
                                        {
                                            src=ruleAssigned;
                                            title="Validation rule assigned ({0})";
                                        }
                                    }
    
                                    if (src === "") return "";
                                    title = Ext.String.format(title, assignments);
    
                                    return  '<div style="text-align:center;height:14px;width:32px;overflow:visible">' +
                                    Ext.String.format('<img style="vertical-align:-3px" src="{0}" title="{1}"/>', src, title) +
                                    '</div>';
    
                                },
                                width: 40,
                                text: 'State'
                            },
                            {
                                xtype: 'templatecolumn',
                                hidden: true,
                                tpl: [
                                    '<div style="text-align:center;height:16px;width:16px;overflow:visible">',
                                    '    <tpl if="warning !== undefined && warning !== null && warning.length &gt; 0">',
                                    '        <img src="images/icons/silk/error.png" title="{warning}"/>',
                                    '    </tpl>',
                                    '</div>'
                                ],
                                width: 26,
                                text: ''
                            },
                            {
                                xtype: 'templatecolumn',
                                tpl: [
                                    '<div style="text-align:center;height:16px;width:16px;overflow:visible">',
                                    '    <tpl if="issue !== undefined && issue !== null">',
                                    '        <tpl if="issue === 1">',
                                    '            <img src="images/icons/silk/exclamation.png" data-qtip=\'Click for details\' "/>',
                                    '        <tpl elseif="issue === 2">',
                                    '            <img src="images/icons/silk/error.png" data-qtip=\'Click for details\' "/>',
                                    '        </tpl>',
                                    '    </tpl>',
                                    '</div>'
                                ],
                                width: 26,
                                text: 'Issue'
                            }
                        ],
                        listeners: {
                            afterlayout: {
                                fn: me.onFormulagridAfterLayout,
                                scope: me
                            }
                        }
                    }
                ]
            });
    
            me.callParent(arguments);
        },
    
        onFormulagridAfterLayout: function(container, layout, eOpts) {
            // this.log("Formula Grid After Layout");
    
            var store = container.getStore();
            var warnings = false;
    
            try
            {
    
                // Evaluate each node using this function recursively
                var f = function(record)
                {
                    try
                    {
                        var data = record.data;
    
                        warnings |= data.warning != undefined && data.warning !== null && data.warning.length > 0;
    
                        if (record.childNodes === undefined || record.childNodes === null)
                        return;
    
                        record.eachChild(f, this);
                    }
                    catch(ex)
                    {
                        // console.log(ex.message);
                    }
                };
    
                f(store.getRootNode());
    
                // Show/hide the warning column
                var cols = Ext.Array.filter(container.columns, function(col) { return col.text === ""});
                if (cols.length === 0) return;
    
                cols[0].setVisible(warnings);
            }
            catch(ex)
            {
                // console.log(ex.message);
            }
    
        }
    
    });
    
    Ext.define('MyApp.view.Viewport', {
        extend: 'MyApp.view.MyViewport',
        renderTo: Ext.getBody()
    });
    
    Ext.onReady(function()
    {
        Ext.Loader.setConfig({
            disableCaching: false,
            enabled: true
        });
    
        Ext.application({
            models: [
                'Formula'
            ],
            stores: [
                'Formulas'
            ],
            autoCreateViewport: true,
            name: 'MyApp'
        });
    });

  2. #2
    Sencha - Ext JS Dev Team evant's Avatar
    Join Date
    Apr 2007
    Location
    Sydney, Australia
    Posts
    17,067
    Vote Rating
    660
    evant has a reputation beyond repute evant has a reputation beyond repute evant has a reputation beyond repute evant has a reputation beyond repute evant has a reputation beyond repute evant has a reputation beyond repute evant has a reputation beyond repute evant has a reputation beyond repute evant has a reputation beyond repute evant has a reputation beyond repute evant has a reputation beyond repute

      0  

    Default


    The issue isn't related to EXTJSIV-9658, the fix for that only touched code to do with the button layout.

    The hide/show could should add a check so it doesn't do the needless operation, but your approach is a fairly dangerous one. Anything in an afterlayout listener that can trigger a layout should be watched pretty carefully, because it's pretty easy to slip into an infinite loop, it's not a pattern I would use.

    A side point; your column renderer does quite a lot of stuff, it's fairly expensive. It would probably be better to do some kind of pre-processing on it.
    Evan Trimboli
    Sencha Developer
    Twitter - @evantrimboli
    Don't be afraid of the source code!

  3. #3
    Sencha Premium Member
    Join Date
    Dec 2011
    Location
    London, UK
    Posts
    260
    Vote Rating
    8
    bseddon will become famous soon enough

      0  

    Default


    <<a dangerous one>>

    Thanks for looking. Yes, I concede you point. The challenge is to know when else the column state can be set. Many activities could cause a layout change and some of those could have changed the issue state of any node. The issue is forcing us to look at setting the column state earlier in the rendering process. Any advice about options will be welcome. ExtJS is a rich frameworks and I know our knowledge only scratches the surface.

    Bill

Thread Participants: 1

Tags for this Thread