Success! Looks like we've fixed this one. According to our records the fix was applied for TOUCH-2387 in a recent build.
  1. #1
    Sencha Premium Member
    Join Date
    Jan 2012
    Posts
    48
    Vote Rating
    1
    hiroprotagonist is on a distinguished road

      0  

    Default NestedList and data disclosure property lead to exception.

    NestedList and data disclosure property lead to exception.


    guys,

    I've found an issue when using the nestedlist in combination with disclosure property on the data.
    Actually there are two ways to use disclosure:
    1. show disclosure on all items on the list
    2. show disclosure only on items where the data of the node has a property called disclosure which must be a boolean.

    I used the second option and run into following issue:
    1. go down the hierarchy of the nested list for minimum two levels (to use the first/ second list that are used internally). where disclosures have been set
    2. go to the third level where minimum one item has no disclosure.
    3. go back to start level.
    On the last back click an exception will be raised in this line:

    (Ext.dataview.element.List: updateListItem method)
    Code:
            if (disclosure && dataview.getOnItemDisclosure()) {
                disclosureEl = extItem.down(me.disclosureClsCache);
                disclosureEl[disclosure ? 'removeCls' : 'addCls'](me.hiddenDisplayCache);
            }
    This issue is raised by this method:

    (Ext.dataview.element.List: getItemElementConfig method)
    Code:
            if (dataview.getOnItemDisclosure()) {
                config.children.push({
                    cls: me.disclosureClsShortCache + ((data.disclosure === false) ? me.hiddenDisplayCache : '')
                });
            }
    As you can see, the classes that are going to be added will miss a space in between which leads to the actual problem that it cannot be found afterwards.

    Version is 2.0.0. RC1, but it looks like issue is still available in release.
    Override can be done like this:
    Code:
    Ext.define('Ext.dataview.element.ListOverride', {     override: 'Ext.dataview.element.List',       getItemElementConfig: function(index, data) {         var me = this,             dataview = me.dataview,             itemCls = dataview.getItemCls(),             cls = me.itemClsShortCache,             config, iconSrc;          if (itemCls) {             cls += ' ' + itemCls;         }          config = {             cls: cls,             children: [{                 cls: me.labelClsShortCache,                 html: dataview.getItemTpl().apply(data)             }]         };          if (dataview.getIcon()) {             iconSrc = data.iconSrc;             config.children.push({                 cls: me.iconClsShortCache,                 style: 'background-image: ' + iconSrc ? 'url("' + newSrc + '")' : ''             });         }          if (dataview.getOnItemDisclosure()) {             config.children.push({                 cls: me.disclosureClsShortCache + ' ' + ((data.disclosure === false) ? me.hiddenDisplayCache : '')             });         }         return config;     } });

  2. #2
    Sencha - Senior Forum Manager mitchellsimoens's Avatar
    Join Date
    Mar 2007
    Location
    Gainesville, FL
    Posts
    36,555
    Vote Rating
    814
    mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute

      0  

    Default


    Nice catch. Here is my test case:

    Code:
    Ext.define('File', {
        extend: 'Ext.data.Model',
        config: {
            fields: [
                {name: 'id',       type: 'string'},
                {name: 'fileName', type: 'string'},
                'disclosure'
            ]
        }
    });
    
    var store = Ext.create('Ext.data.TreeStore', {
        model: 'File',
        autoLoad: true,
        proxy: {
            type: 'ajax',
            url: 'data/json.json'
        }
    });
    
    Ext.create('Ext.NestedList', {
        fullscreen: true,
        title: 'src/',
        displayField: 'fileName',
        listConfig : {
            onItemDisclosure : true
        },
        // add a / for folder nodes in title/back button
        getTitleTextTpl: function() {
            return '{' + this.getDisplayField() + '}<tpl if="leaf !== true">/</tpl>';
        },
        // add a / for folder nodes in the list
        getItemTextTpl: function() {
            return '{' + this.getDisplayField() + '}<tpl if="leaf !== true">/</tpl>';
        },
        store: store
    });
    With JSON:

    Code:
    {
        "children": [
            {
                "cls": "file",
                "fileName": "Ajax.js",
                "id": "./Ajax.js",
                "leaf": true,
                "disclosure" : false
            },
            {
                "cls": "folder",
                "fileName": "form",
                "id": "./form",
                "children": [
                    {
                        "fileName": "Panel.js",
                        "id": "form/Panel.js",
                        "leaf": true,
                        "cls": "file",
                        "disclosure" : false
                    },
                    {
                        "fileName": "Field",
                        "id": "field/Field",
                        "cls": "folder",
                        "children": [
                            {
                                "fileName": "Number.js",
                                "id": "field/Number.js",
                                "leaf": true,
                                "cls": "file"
                            },
                            {
                                "fileName": "Text.js",
                                "id": "field/Text.js",
                                "leaf": true,
                                "cls": "file",
                                "disclosure" : false
                            }
                        ]
                    }
                ]
            },
            {
                "cls": "folder",
                "fileName": "util",
                "id": "./util",
                "children": [
                    {
                        "fileName": "TapRepeater.js",
                        "id": "util/TapRepeater.js",
                        "leaf": true,
                        "cls": "file"
                    }
                ]
            }
        ],
        "text": "."
    }
    I go to form -> field and then try to go back twice and I get the removeCls error. With your override it looks to work so we will look at it! Thank you.

    btw, here is your override but formatted:

    Code:
    Ext.define('Ext.dataview.element.ListOverride', {
        override: 'Ext.dataview.element.List',
        getItemElementConfig: function(index, data) {
            var me = this,
                dataview = me.dataview,
                itemCls = dataview.getItemCls(),
                cls = me.itemClsShortCache,
                config, iconSrc;
    
            if (itemCls) {
                cls += ' ' + itemCls;
            }
    
            config = {
                cls: cls,
                children: [{
                    cls: me.labelClsShortCache,
                    html: dataview.getItemTpl().apply(data)
                }]
            };
    
            if (dataview.getIcon()) {
                iconSrc = data.iconSrc;
    
                config.children.push({
                    cls: me.iconClsShortCache,
                    style: 'background-image: ' + iconSrc ? 'url("' + newSrc + '")' : ''
                });
            }
    
            if (dataview.getOnItemDisclosure()) {
                config.children.push({
                    cls: me.disclosureClsShortCache + ' ' + ((data.disclosure === false) ? me.hiddenDisplayCache : '')
                });
            }
    
            return config;
        }
    });
    Mitchell Simoens @SenchaMitch
    Sencha Inc, Senior Forum Manager
    ________________
    Check out my GitHub, lots of nice things for Ext JS 4 and Sencha Touch 2
    https://github.com/mitchellsimoens

    Think my support is good? Get more personalized support via a support subscription. https://www.sencha.com/store/

    Need more help with your app? Hire Sencha Services services@sencha.com

    Want to learn Sencha Touch 2? Check out Sencha Touch in Action that is in print!

    When posting code, please use BBCode's CODE tags.

  3. #3
    Sencha Premium Member
    Join Date
    Jan 2012
    Posts
    48
    Vote Rating
    1
    hiroprotagonist is on a distinguished road

      0  

    Default


    Actually I found a second issue right in the same class.
    There is an issue updating disclosure items when moving between the pages.

    As I already mentioned above I use the disclosure like this:
    2.. show disclosure only on items where the data of the node has a property called disclosure which must be a boolean.

    Follow these steps to reproduce:
    1. go down the hierarchy of the nested list for minimum two levels (to use the first/ second list that are used internally). where disclosures have been set. Make sure that on the last page there is at least one item that has disclosure set to false. (so there are not all disclosures shown). It does make sense to have the first item set to false as you'll only see the issue when the item is updated later on.
    2. go back to root level.
    3. the item that does not have the disclosure item was incorrectly updated, missing disclosure item now.

    Original code looks like this (line 58786, Ext.dataview.element.List.updateListItem):
    Code:
            if (disclosure && data[disclosureProperty] === false) {
                disclosureEl = extItem.down(me.disclosureClsCache);
                disclosureEl[disclosure ? 'removeCls' : 'addCls'](me.hiddenDisplayCache);
            }
    Here as usual the fix.(comments highlighted)

    Code:
    Ext.define('Flexreport.overrides.ExtdataviewelementList', {
        override: 'Ext.dataview.element.List',
    
        getItemElementConfig: function (index, data) {
            var me = this,
                dataview = me.dataview,
                itemCls = dataview.getItemCls(),
                cls = me.itemClsShortCache,
                config, iconSrc;
    
            if (itemCls) {
                cls += ' ' + itemCls;
            }
    
            config = {
                cls: cls,
                children: [{
                    cls: me.labelClsShortCache,
                    html: dataview.getItemTpl().apply(data)
                }]
            };
    
            if (dataview.getIcon()) {
                iconSrc = data.iconSrc;
                config.children.push({
                    cls: me.iconClsShortCache,
                    style: 'background-image: ' + iconSrc ? 'url("' + newSrc + '")' : ''
                });
            }
    
            if (dataview.getOnItemDisclosure()) {
                // actually here is an issue as the original code missing the space between the classes.
                config.children.push({
                    cls: me.disclosureClsShortCache + ' ' + ((data.disclosure === false) ? me.hiddenDisplayCache : '')
                });
            }
            return config;
        },
        updateListItem: function(record, item) {
            var me = this,
                dataview = me.dataview,
                extItem = Ext.fly(item),
                innerItem = extItem.down(me.labelClsCache, true),
                data = record.data,
                disclosureProperty = dataview.getDisclosureProperty(),
                disclosure = data && data.hasOwnProperty(disclosureProperty),
                iconSrc = data && data.hasOwnProperty('iconSrc'),
                disclosureEl, iconEl;
    
            innerItem.innerHTML = dataview.getItemTpl().apply(data);
            
            // actually here is a difference, original code additionally asks for the value of disclosure is false which is not sensible as this
            // case here handles both.
            if (disclosure) {
                disclosureEl = extItem.down(me.disclosureClsCache);
                disclosureEl[disclosure ? 'removeCls' : 'addCls'](me.hiddenDisplayCache);
            }
    
            if (dataview.getIcon()) {
                iconEl = extItem.down(me.iconClsCache, true);
                iconEl.style.backgroundImage = iconSrc ? 'url("' + iconSrc + '")' : '';
            }
        }
    
    });

  4. #4
    Sencha - Sencha Touch Dev Team Jamie Avins's Avatar
    Join Date
    Mar 2007
    Location
    Redwood City, California
    Posts
    3,661
    Vote Rating
    21
    Jamie Avins is a jewel in the rough Jamie Avins is a jewel in the rough Jamie Avins is a jewel in the rough Jamie Avins is a jewel in the rough

      0  

    Default


    In updateListItem, I believe you have a slight problem. I have updated the variables to make it a bit clearer and fixed the logic:

    Code:
        updateListItem: function(record, item) {
            var me = this,
                dataview = me.dataview,
                extItem = Ext.fly(item),
                innerItem = extItem.down(me.labelClsCache, true),
                data = record.data,
                disclosureProperty = dataview.getDisclosureProperty(),
                hasDisclosureProperty = data && data.hasOwnProperty(disclosureProperty),
                iconSrc = data && data.hasOwnProperty('iconSrc'),
                disclosureEl, iconEl;
    
            innerItem.innerHTML = dataview.getItemTpl().apply(data);
    
            if (hasDisclosureProperty) {
                disclosureEl = extItem.down(me.disclosureClsCache);
                disclosureEl[data[disclosureProperty] === false ? 'removeCls' : 'addCls'](me.hiddenDisplayCache);
            }
    
            if (dataview.getIcon()) {
                iconEl = extItem.down(me.iconClsCache, true);
                iconEl.style.backgroundImage = iconSrc ? 'url("' + iconSrc + '")' : '';
            }
        },

    Sencha Inc

    Jamie Avins

    @jamieavins

  5. #5
    Sencha Premium Member
    Join Date
    Jan 2012
    Posts
    48
    Vote Rating
    1
    hiroprotagonist is on a distinguished road

      0  

    Default


    I just took the code and fixed the issue.
    Thanks Jamie, that looks better.

    *update*: I just tried the code out and it didn't work. No time to check for the issue, will stay on mine.

Thread Participants: 2