Keeping track of the selected tab of an apex:tabPanel in the controller

PS Read the next few paragraphs for some context but then move on to the better implementation described in Keeping track of the selected tab of an apex:tabPanel in the controller – using Javascript remoting.

I have a heavily used page that presents a dozen or so fairly complex panels using apex:tab/apex:tabPanel. The controller can set the tab that is initially displayed by providing a property that is used for the “value” (not the “selectedTab”) of the apex:tabPanel. But I also want tab selections made by the user to be recorded back on the controller (and then persisted). This is to improve usability so that the next time the user returns to the page they can continue where they left off. This approach also avoids the need to encode the current tab selection in saveURL and retURL parameters that are part of links to allow child objects to be added or edited and then the same tab returned to when a Save or Cancel is done.

One approach I found suggested is to use a “switchType” of “server” or “ajax” instead of “client”. For my page using either of these settings changed the instantaneous tab switch of the “client” setting to a glacial and feedback-less several second delay in the tab switch. I also found postings that implied that “client” would write the current tab name back to the controller but in my tests it did not.

If you know of a recently tested platform-supported way to do this please comment. Meanwhile here is what I ended up doing.

The problem has two parts:

  • finding which tab is currently selected
  • writing the name of that tab back to the controller

Solving the first problem means inspecting the platform’s implementation classes and so will break if they change. Solving the second problem makes use of published platform functionality and Matt Lacey’s apex:actionFunction and apex:param – Or How to Pass Values from Javascript to an Apex Controller.

Here is an outline of the Visualforce:

<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:actionFunction id="af" name="selectTabActionFunction" action="{!selectTab}" reRender="">
            <apex:param name="selectedTab" assignTo="{!selectedTab}" value="{!selectedTab}"/>
        </apex:actionFunction>
    </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() {
    selectTabActionFunction(getSelectedTabName());
}
</script>
</apex:page>

and an outline of the corresponding controller Apex:

public with sharing class XyzController  {
    private Xyz__c xyz;
    // "Get" called when page rendered; "set" called by client-side Javascript
    public String selectedTab {
        get {
            if (selectedTab == null) {
                if (xyz.LastViewedTab__c != null) {
                    // Select the last saved tab
                    selectedTab = xyz.LastViewedTab__c;
                } else {
                    // Select a default tab
                    selectedTab = 'Tab1';
                }
            }
            return selectedTab;
        }
        set {
            if (value != selectedTab) {
                // Persist just the tab change
                upsert new Xyz__c(Id = xyz.Id, LastViewedTab__c = value);
            }
            selectedTab = value;
        }
    }
    // Method for client-side Javascript to invoke; does nothing
    public PageReference selectTab() {
        return null;
    }
    // Other code...
}

Note that the “onclick” event on apex:tabPanel happens after the selected tab has already switched ensuring it is the new tab name that is written to the controller not the old tab name. And the performance of the write to the controller is good enough that the tab switch remains pretty much instantaneous.

Advertisements

2 thoughts on “Keeping track of the selected tab of an apex:tabPanel in the controller

  1. 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… ??

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