JDeveloper 12.2.1.4: af:panelTabbed

The new JDeveloper 12.2.1.4 provides some new features and enhancements. In the next couple of posts we are going to investigate some of them.

We start with the af:panelTabbed component. There is an enhancement fro this component we all waited for a long time. Vertical stacked tabs with icons and text. This sounds like a small thing, but in earlier versions of ADF vertical stacked tabs could only show icons.

We are calling tabs vertical stacked, if the tab is on the left or right side of the panel. Horizontal stacked tabs are the ones at the top or the button of the panel.

Vertical Stacked Tab with Icons and Text

As said before, in earlier versions of ADF you needed to do a workaround to get tabs on the left or right showing an icon, text or both. You needed to create an icon from the icon, Text or icon with text and add it to the af:showDetailItem. The drawback was that you couldn’t just edit the text (e.g. correcting a spelling mistake) or had to generate different such icons for different languages along with some logic to exchange the icon according to the selected language.

The new af:panelTabbed has a new property ‘verticalTabMode‘ with two modes:

  • iconOnly this mode behaves like the old versions and only shows an icon defined for the af:showDetailItem
  • iconAndText this mode shows an icon and the text defined in the text property of the af:showDetailItem
af:panelTabbed with verticalTabMode=”iconOnly’
af:panelTabbed with verticalTabMode=”iconAndText’

The images above show the af:panelTabbed with the new verticalTabMode. The sample application for this can be downloaded from GitHub BlogPanelTabbed. It does not use a DB connection and was built using JDeveloper 12.2.1.4.

JDev 12.1.3: Using Parent Action to Navigate Tabs of a af:panelTabbed from Inside a Region

This blog is based on a question in the OTN JDeveloper and ADF forum. The Question was how to navigate from one selected tab to the next tab when the af:showDetailItem in the tab is a region and the button to navigate is inside the region.

We implement two cases, the first is the easy one where the button to navigate is in the page holding the af:panelTabbed. The second one uses a button is inside a bounded task flow which is shown in the af:showDetailItem in a tab to navigate the af:panelTabbed.

We start with creating a new ‘ADF Fusion Web Application’ from the gallery. We only change the application name and the path of the application, otherwise we can just use the default values. The sample is simple and doesn’t need a model project or connection to a DB. You can download the finished workspace using the link provided at the end of the post.

We skip all the needed steps and going right into creating the starting page which holds the af:panelTabbed. It has three af:showDetailItem and a af:Button to navigate the tabs directly from the page. This button implements the first use case.

Start Page with Outer Navigation

Start Page with Outer Navigation

The button has a listener attached which is implemented in a viewScope bean ‘NavigateTabBean’. The listener implements the needed logic to navigate from the selected tab to the next tab. If the last tab is reached the first tab is selected.

    private static ADFLogger _logger = ADFLogger.createADFLogger(NavigateTabBean.class);
    private static final String PANELTAB = "pt1";

    /**
     * Eventhandler to navigate to the next tab in a af:panelTabbed
     * @param actionEvent event which called the listener
     */
    public void naviGateButtonAction(ActionEvent actionEvent) {
        UIComponent ui = getUIComponent(PANELTAB);
        if (ui == null) {
            _logger.info("PanelTab component not found!");
            return;
        }
        if (!(ui instanceof RichPanelTabbed)) {
            _logger.info("Component is not an af:panelTabbed");
            return;
        }

        RichPanelTabbed rpt = (RichPanelTabbed) ui;
        int childCount = rpt.getChildCount();
        List<UIComponent> children = rpt.getChildren();
        for (int ii = 0; ii < childCount; ii++) {
            UIComponent uiSDI = children.get(ii);
            if (uiSDI instanceof RichShowDetailItem) {
                RichShowDetailItem rsdi = (RichShowDetailItem) uiSDI;
                if (rsdi.isDisclosed()) {
                    //close current tab
                    rsdi.setDisclosed(false);
                    //calculate next tab to disclose as next_tab_index = (current_tab_index + 1) % number_of_tabs
                    int kk = ii + 1;
                    int jj = kk % childCount;
                    _logger.info("old disclosed tab: " + ii + " new disclodes tab: " + jj);
                    RichShowDetailItem newSDI = (RichShowDetailItem) children.get(jj);
                    //open new tab
                    newSDI.setDisclosed(true);
                    AdfFacesContext.getCurrentInstance().addPartialTarget(rpt);
                    return;
                }
            }
        }
    }

    // find a jsf component
    private UIComponent getUIComponent(String name) {
        FacesContext facesCtx = FacesContext.getCurrentInstance();
        return facesCtx.getViewRoot().findComponent(name);
    }

    public void nextTab() {
        naviGateButtonAction(null);
    }

The logic in the action listener first searches for the af:panelTabbed in the viewRoot and gets the number of children from it. Each child is one of the af:showDetailItem representing a tab. Then we iterate over the child list and search the currently disclosed tab. We close this tab and the next tab in the list gets disclosed. If the currently selected tab is the last in the list, the first tab is disclosed (see the comments in the code section).

To Implement the second use case, the one we really want to talk about, we first need to implement three bounded task flows which we later use as regions in the tabs.

Bounden Task Flow with Parent Action

Bounden Task Flow with Parent Action

The image shows the bounded task flow for one tab. The other bounded task flows are build in the same way and are just showing different text. The reason for this is that you normally would use different regions aka different task flows in the tabs. We could have used only one bounded task flow with a parameter to change the text shown in the fragment. In the sample you’ll find this implemented for tabs 4 and 5.
The region is simple and only shows one fragment which has a button to navigate to the next tab and a test to distinguish the regions when navigating. The whole magic is the parent action in the bounded task flow. This parent action executes a navigation case ‘nextTab’ in the parent task flow.

Unbounded Task Flow with Start Page

Unbounded Task Flow with Start Page

In the image above we the the unbounded task flow which is the parent of the bounded task flow. Here a wild card rule navigates to a method call activity ‘selectNextTab’ using the navigation case ‘nextTab’ we entered to the parent action of the regions.
The method action calls the ‘nextTab()’ in the managed bean from the code section above. All this method does is to call the action listener which is called from the af:Button of the start page (Start.jsf). As the action listener needs an ActionEvent as parameter, which we don’t use in the code we pass ‘null’ when we call the listener from the method call activity.

This concludes the implementation. Here are some images from the running application

The sample application can be downloaded form ADFEMG Sample Project.

A version of the software build with JDeveloper 11.1.1.7.0 can be downloaded fromĀ GitHub