ICEfaces
  1. ICEfaces
  2. ICE-7627

ace:tabPane - dynamically changing disabled attribute doesn't allow selection of tab

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: EE-2.0.0.GA, 3.0.RC1, 3.0.RC2
    • Fix Version/s: 3.0
    • Component/s: ACE-Components
    • Labels:
      None
    • Environment:
      ICEfaces EE 2.0, ICEfaces 3.0RC
    • Assignee Priority:
      P1

      Description

      Dynamically changing the disabled attribute on the ace:tabPane component does not allow the tab to be selected after it is activated. The tab is shown as being selectable but selection does not happen. If the browser is refreshed then the tabs can now be selected.

        Activity

        Hide
        Arran Mccullough added a comment -

        Attached source code and built war file to reproduce issue.

        Steps to reproduce:

        • Load app, at the top of the page there are four commandButtons, each one will activate its respective tab.
        • The tabSet is loaded as all disabled by default.
        • Click on the "Activate Tab One" button and the "Activate Tab Two" button.
        • Tab One and Tab Two are now shown as being selectable, but their content is not shown.
        • Try and select either one of these tabs, nothing happens.
        • Refresh the browser, the tabs content is now shown and they can be selected.
        Show
        Arran Mccullough added a comment - Attached source code and built war file to reproduce issue. Steps to reproduce: Load app, at the top of the page there are four commandButtons, each one will activate its respective tab. The tabSet is loaded as all disabled by default. Click on the "Activate Tab One" button and the "Activate Tab Two" button. Tab One and Tab Two are now shown as being selectable, but their content is not shown. Try and select either one of these tabs, nothing happens. Refresh the browser, the tabs content is now shown and they can be selected.
        Hide
        Mark Collette added a comment -

        Bugs about dynamic properties are generally caused by the change of property not causing a sufficient Dom update. Here, it's likely that the tapPane renders some disabled style, but that the tabSet's JavaScript still needs to execute to make the relevant JavaScript objects know it's disabled. Forcing the JavaScript to update, given some property change that's not rendered in the script tag is generally accomplished by adding the property to the hash field.

        Show
        Mark Collette added a comment - Bugs about dynamic properties are generally caused by the change of property not causing a sufficient Dom update. Here, it's likely that the tapPane renders some disabled style, but that the tabSet's JavaScript still needs to execute to make the relevant JavaScript objects know it's disabled. Forcing the JavaScript to update, given some property change that's not rendered in the script tag is generally accomplished by adding the property to the hash field.
        Hide
        Nils Lundquist added a comment -

        I've attempted the technique Mark suggests, but it was complicated by the fact that the TabPane doesn't have a JS implementation supplied by ICEfaces, it is just the basic YUI component. The disabled state is determined in initialization of the parent tabset according to the presence of the disabled style class on the tab markup, as rendered by the TabSetRenderer (read from TabPane.isDisabled()). My plan was to have the TabSetRenderer calculate a hash of all the disabled values of the TabPanes, and have the JS 'refresh' the state of the tabs whenever it changed.

        Without a ICEfaces instance of TabPane available, I referenced the TabSet instance, used it to reference the YUI tab, and attempted to set disabled that way. This worked (when checked afterwards with Tab.get('disabled')), however the tabset became totally unresponsive after any use of ice.ace.tabset.getInstance.

        Show
        Nils Lundquist added a comment - I've attempted the technique Mark suggests, but it was complicated by the fact that the TabPane doesn't have a JS implementation supplied by ICEfaces, it is just the basic YUI component. The disabled state is determined in initialization of the parent tabset according to the presence of the disabled style class on the tab markup, as rendered by the TabSetRenderer (read from TabPane.isDisabled()). My plan was to have the TabSetRenderer calculate a hash of all the disabled values of the TabPanes, and have the JS 'refresh' the state of the tabs whenever it changed. Without a ICEfaces instance of TabPane available, I referenced the TabSet instance, used it to reference the YUI tab, and attempted to set disabled that way. This worked (when checked afterwards with Tab.get('disabled')), however the tabset became totally unresponsive after any use of ice.ace.tabset.getInstance.
        Hide
        Nils Lundquist added a comment -

        I attempted again, and had greater success but my results appears to indicate that we need to send disabled property per-tab rather than as a hash.

        After adding:
        if (oldJSFProps && oldJSFProps['hashCode'] != jsfProps['hashCode']) {
        var tabs = context.getComponent().get('tabs');
        for (var i = 0; i < tabs.length; i++)

        { alert(tabs[i].get('disabled') + ' : ' + tabs[i].hasClass('ui-state-disabled')); }

        }

        to ice.ace.tabset.updateProperties, I discovered that while the attached updateProperties script is run for every tabset render, it is run before the update is applied to the DOM. This means that though we are trying to inspect which tab has been disabled/enabled, and carry over the change into JS, the JS is running before the change to the style class, and that style class change is the one indication on the DOM of which TabPane is responsible for the hash code change. Without sending per-tab information, or running some sort of event following the DOM update, I'm not sure how this will be possible.

        Show
        Nils Lundquist added a comment - I attempted again, and had greater success but my results appears to indicate that we need to send disabled property per-tab rather than as a hash. After adding: if (oldJSFProps && oldJSFProps ['hashCode'] != jsfProps ['hashCode'] ) { var tabs = context.getComponent().get('tabs'); for (var i = 0; i < tabs.length; i++) { alert(tabs[i].get('disabled') + ' : ' + tabs[i].hasClass('ui-state-disabled')); } } to ice.ace.tabset.updateProperties, I discovered that while the attached updateProperties script is run for every tabset render, it is run before the update is applied to the DOM. This means that though we are trying to inspect which tab has been disabled/enabled, and carry over the change into JS, the JS is running before the change to the style class, and that style class change is the one indication on the DOM of which TabPane is responsible for the hash code change. Without sending per-tab information, or running some sort of event following the DOM update, I'm not sure how this will be possible.
        Hide
        Nils Lundquist added a comment -

        Problem now lies with the DOMEventHandler for the tabset. It's eating the events for the newly enabled tabs.

        Show
        Nils Lundquist added a comment - Problem now lies with the DOMEventHandler for the tabset. It's eating the events for the newly enabled tabs.
        Hide
        Nils Lundquist added a comment -

        DOMEventHandler required that the DOM element causing the event was the child of one of the tabs elements that was initialized with tabset. The tab has been updated since the initialization due to the disabled style class changing on the server, so the DOMEventHandler didn't find a parent tab and ate the event.

        When setAttributeConfig('disabled',

        {value: newDOMElem}

        , true) was used to configure the tab JS object with the updated element, the DOMEventHandler passed the events on to the tab correctly, however it now appears the tab DOM element hasn't been configured with all the YUI Tab JS synthetic events. YUI sure doesn't make anything easy.

        Not sure how to rebind these events to the new DOM element so, to avoid the problem above entirely, I'm attempting to modify the way in which tabs are disabled to avoid the DOM update, and let the tabset JS solely handle tab disabling.

        I'd attempted to remove the tabs following the DOM update and adding the new DOM element as a 'new' tab at the old index, however the out-of-sync Tab object cannot be removed from the tabset either, due to an error related to the tabset attempting to remove the absent DOM element from its subtree.

        Show
        Nils Lundquist added a comment - DOMEventHandler required that the DOM element causing the event was the child of one of the tabs elements that was initialized with tabset. The tab has been updated since the initialization due to the disabled style class changing on the server, so the DOMEventHandler didn't find a parent tab and ate the event. When setAttributeConfig('disabled', {value: newDOMElem} , true) was used to configure the tab JS object with the updated element, the DOMEventHandler passed the events on to the tab correctly, however it now appears the tab DOM element hasn't been configured with all the YUI Tab JS synthetic events. YUI sure doesn't make anything easy. Not sure how to rebind these events to the new DOM element so, to avoid the problem above entirely, I'm attempting to modify the way in which tabs are disabled to avoid the DOM update, and let the tabset JS solely handle tab disabling. I'd attempted to remove the tabs following the DOM update and adding the new DOM element as a 'new' tab at the old index, however the out-of-sync Tab object cannot be removed from the tabset either, due to an error related to the tabset attempting to remove the absent DOM element from its subtree.
        Hide
        Nils Lundquist added a comment -

        I've made some success with the last technique I mention above.

        The tabset diffs the updated set of disabled indexes with old one (if one exists) and uses JS to disable the individual tabs.

        This solution unfortunately allows the selectedIndex to be loaded at the tabset construction, even though it may be disabled. This may be able to be corrected.

        There may also be styling difference between this client-managed approach and the previous server-managed disabled styling.

        Show
        Nils Lundquist added a comment - I've made some success with the last technique I mention above. The tabset diffs the updated set of disabled indexes with old one (if one exists) and uses JS to disable the individual tabs. This solution unfortunately allows the selectedIndex to be loaded at the tabset construction, even though it may be disabled. This may be able to be corrected. There may also be styling difference between this client-managed approach and the previous server-managed disabled styling.
        Hide
        Nils Lundquist added a comment -

        The styling issues I mention above aren't of concern. The fixed version looks fine.

        However the on-construction selection of the selectedIndex that occurs before our JS can apply the disabled property, will be very problematic. I don't see how this could be avoided without large modifications to the YUI code, or fixing the larger issue using yet some other technique.

        The issue is we want to initialize the tabs with the disabled style class so the YUI construction won't do an initial selection. However later when we remove that disabled style class and enable the tab, we don't want an update (that kills our events), or that update has to somehow play nice with YUI, and rebind the new DOM element.

        Show
        Nils Lundquist added a comment - The styling issues I mention above aren't of concern. The fixed version looks fine. However the on-construction selection of the selectedIndex that occurs before our JS can apply the disabled property, will be very problematic. I don't see how this could be avoided without large modifications to the YUI code, or fixing the larger issue using yet some other technique. The issue is we want to initialize the tabs with the disabled style class so the YUI construction won't do an initial selection. However later when we remove that disabled style class and enable the tab, we don't want an update (that kills our events), or that update has to somehow play nice with YUI, and rebind the new DOM element.
        Hide
        Nils Lundquist added a comment -

        ICE-7627 - Partial fix for dynamic tabpane disabled issues. Using JS-managed state as DOM update ruined the existing Tab object, and I could not find a way to rebind the Tab object to the new DOM element; and removing the now DOM-less Tab from the TabView caused an error, so a newly created Tab object could not be added in its place. This change to JS initialized state however has introduced another problematic behaviour, that the selectedIndex is always loaded at tabset creation even though it is disabled by further constructor calls shortly after. Either the disabling will need to occur at Tab creation, during TabSet creation, or eventually the old technique will need to be used, and must elegantly update or replace the existing Tab object, rather than breaking it.

        Show
        Nils Lundquist added a comment - ICE-7627 - Partial fix for dynamic tabpane disabled issues. Using JS-managed state as DOM update ruined the existing Tab object, and I could not find a way to rebind the Tab object to the new DOM element; and removing the now DOM-less Tab from the TabView caused an error, so a newly created Tab object could not be added in its place. This change to JS initialized state however has introduced another problematic behaviour, that the selectedIndex is always loaded at tabset creation even though it is disabled by further constructor calls shortly after. Either the disabling will need to occur at Tab creation, during TabSet creation, or eventually the old technique will need to be used, and must elegantly update or replace the existing Tab object, rather than breaking it.
        Hide
        Nils Lundquist added a comment - - edited

        Reassigning to Mark for eventual revision to fix selectedIndex initialization.

        More extensive YUI knowledge will be required to gracefully update/replace the Tab object that has been destroyed by the DOM update, or modify the TabView construction so that my technique of JS disabled initialization occurs soon enough to prevent the invalid initial selection.

        Show
        Nils Lundquist added a comment - - edited Reassigning to Mark for eventual revision to fix selectedIndex initialization. More extensive YUI knowledge will be required to gracefully update/replace the Tab object that has been destroyed by the DOM update, or modify the TabView construction so that my technique of JS disabled initialization occurs soon enough to prevent the invalid initial selection.
        Hide
        Nils Lundquist added a comment -

        Added custom styling and backported change to 2.X EE maintenance.

        Show
        Nils Lundquist added a comment - Added custom styling and backported change to 2.X EE maintenance.
        Hide
        Nils Lundquist added a comment -

        Fixed again in this other branch.

        Revision #27275
        ICE-7627 - Backporting tabset dynamic disabled attribute fix.

        Show
        Nils Lundquist added a comment - Fixed again in this other branch. Revision #27275 ICE-7627 - Backporting tabset dynamic disabled attribute fix.

          People

          • Assignee:
            Nils Lundquist
            Reporter:
            Arran Mccullough
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: