Keeping track of the selected tab of an apex:tabPanel in the controller – using Javascript remoting

PS See BarryC’s comments below about only invoking the remoting when the tab label is clicked.

While this Keeping track of the selected tab of an apex:tabPanel in the controller implementation works, taking a look at the “Network” view of Chrome’s “Developer Tools” brings the bad news that the call to the server made when a tab is clicked on involves (in my case) a 700k byte data transfer and an elapsed time of around 3 seconds. This is a lot of overhead to just transmit the name of the selected tab to the server. This cost and time delay is not immediately apparent because the browser remains responsive. Looking at the payload sent to the server, the problem is that the full view state is being sent as part of the request.

The “writing the name of that tab back to the controller” technique I should have used in the first place is JavaScript remoting. Changing to that reduces the data transfer size to a few hundred bytes and the elapsed time to a few hundred milliseconds.

Here is the changed Visualforce and Apex:

<apex:page id="p" standardController="Xyz__c" extensions="XyzController">
    <apex:form id="f">
        <apex:tabPanel id="tp" switchType="client" value="{!selectedTab}" onclick="setSelectedTabOnController()">
            <apex:tab name="Tab1" label="Tab1" id="Tab1">
                <!-- Tab content -->
            </apex:tab>
            <apex:tab name="Tab2" label="Tab2" id="Tab2">
                <!-- Tab content -->
            </apex:tab>
        </apex:tabPanel>
    </apex:form>
<script type="text/javascript">
function getSelectedTabName() {
    if (RichFaces) {
        var tabs = RichFaces.panelTabs['p:f:tp'];
        for (var i = 0; i < tabs.length; i++) {
            var tab = tabs[i];
            if (RichFaces.isTabActive(tab.id + '_lbl')) {
                return tab.name;
            }
        }
    }
    return null;
}
function setSelectedTabOnController() {
    // Invoke server remote action method
    XyzController.updateLastViewedTab('{!Xyz__c.Id}', getSelectedTabName(), callbackHandler)
}
function callbackHandler(result, event) {
    if (event.type == 'exception') {
        alert(event.message);
    }
}
</script>
</apex:page>
global with sharing class XyzController  {
    private Xyz__c xyz;
    @RemoteAction
    global static void updateLastViewedTab(Id xyzId, String tabName) {
    	upsert new Xyz__c(Id = xyzId, LastViewedTab__c = tabName);
    }
    public String selectedTab {
        get {
            if (selectedTab == null) {
                if (xyz.LastViewedTab__c != null) {
                    selectedTab = xyz.LastViewedTab__c;
                } else {
                    selectedTab = 'Tab1';
                }
            }
            return selectedTab;
        }
        // Setter has to be public as some (but not all) actions do result in an update
        set;
    }
    // Other code...
}
Advertisements

7 thoughts on “Keeping track of the selected tab of an apex:tabPanel in the controller – using Javascript remoting

    • I have the same Requirement , Tried your code But facing an ERROR!
      System.NullPointerException: Attempt to de-reference a null object
      Class.XyzController.__sfdc_selectedTab: line 14, column 1

      How to overcome this issue and is LastViewedTab__c a Field in Xyz__c
      How to resolve this… ??

      • Part of the “Other code” that I didn’t show needs to be code that does a query (including the field LastViewedTab__c) that is used to set the xyz variable when the controller initializes.

  1. The onclick event on apex:tabpanel will fire when a user clicks anywhere within that panel, not just the tab labels. You can overcome this by moving the event from the apex:tabpanel to the apex:tab elements as shown here.

    I had issues with the event firing before the tab gets marked as the active one, which caused the getSelectedTabName function to return the last tab rather than the tab the user just selected.

    The solution was to pass in the tab name directly in the cvSetSelectedTabOnController call. Heres the updated function:

    function setSelectedTabOnController(tabName) {
    // Invoke server remote action method
    XyzController.updateLastViewedTab(‘{!Xyz__c.Id}’, tabName callbackHandler)
    }

    • It looks like wordpress stripped out my apex:tab element in my above comment. Heres how the apex:tab looks without the opening and closing brackets:

      apex:tab name=”Tab1″ label=”Tab1 ontabenter=”cvSetSelectedTabOnController(‘Tab1’)”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s