In this article, I describe how to build what I call a ‘Badge Button’ in Oracle ADF. This is a button we know from many mobile applications which shows, e.g., how many new messages have arrived since the last visit.

The image above shows what I have in mind. We see a standard button with a badge showing the number of new items. The badge can be used to display text as well:

The idea behind this came from a question on the ODC JDev & ADF space. When I first read the question I wanted to answer “No, there isn’t such a component in ADF”, but after thinking about it, I decided to build one myself.

The Problem has two parts:

  • How to build the badge
  • How to create the component looking like a button with a badge

Solution Part 1

Building the badge turned out to be very easy. Searching the web, you find plenty of solutions for such components using CSS. As you might know, it’s not very straightforward to add a CSS solution to ADF. However, there are ways to do this. In the end, I used the following CSS to generate the badge:

.badge {
    background:             radial-gradient( 5px -9px, circle, white 8%, red 26px );
    background:    -moz-radial-gradient( 5px -9px, circle, white 8%, red 26px );
    background:     -ms-radial-gradient( 5px -9px, circle, white 8%, red 26px );
    background:      -o-radial-gradient( 5px -9px, circle, white 8%, red 26px );
    background: -webkit-radial-gradient( 5px -9px, circle, white 8%, red 26px );
    background-color: red;
    border: 2px solid white;
    border-radius: 12px; /* one half of ( (border * 2) + height + padding ) */
    box-shadow: 1px 1px 1px black;
    color: white;
    font: bold 15px/13px Helvetica, Verdana, Tahoma;
    height: 16px; 
    padding: 4px 3px 0 3px;
    text-align: center;
    min-width: 14px;
    margin: 0px 0px 20px -10px;
    position: relative;
}

Solution Part 2

ADF uses CSS which is built from skin files. It’s not straightforward to add any other CSS to a component as you don’t see the generated HTML where you need to add the CSS. So adding the CSS class just as style to the button doesn’t work, as you can’t add new properties to ADF components. You will get an error like:

Error initializing CSS

Error initializing CSS

To create a badge, you need to create a span or div with the CSS class and some data which is put into the badge like

<div class=”badge”>4</div>

This can’t be added to an existing component easily. However, ADF has a component which renders as a div tag, the af:outputText. If you add an af:outputText to a page and look at the resulting HTML you get

<div>outputText</div>

So, the value of the outputText is just surrounded by a div tag. The problem still is that you can’t add your own property to the ADF component, but we can add the div with the needed style and the data as the value of the af:outputText component. All we have to do, other than to add the div is to set the escape property of the outputText to false. This setting avoids that ADF encodes the value which would transform the ‘<’ into ‘&lt;’, essentially making a string out of the value.  The reason for that is to prevent security issues (injection), so be aware of this!

Putting this on a page would look like

  <f:view>
    <af:document id="d1">
      <af:resource type="css">
        .badge {
        background:             radial-gradient( 5px -9px, circle, white 8%, red 26px );
            background:    -moz-radial-gradient( 5px -9px, circle, white 8%, red 26px );
            background:     -ms-radial-gradient( 5px -9px, circle, white 8%, red 26px );
            background:      -o-radial-gradient( 5px -9px, circle, white 8%, red 26px );
            background: -webkit-radial-gradient( 5px -9px, circle, white 8%, red 26px );
        background-color: red;
        border: 2px solid white;
        border-radius: 12px; /* one half of ( (border * 2) + height + padding ) */
        box-shadow: 1px 1px 1px black;
        color: white;
        font: bold 15px/13px Helvetica, Verdana, Tahoma;
        height: 16px; 
        padding: 4px 3px 0 3px;
        text-align: center;
        min-width: 14px;
        margin: 0px 0px 20px -10px;
        position: relative;
    }
    </af:resource>
    <af:form id="f1">
        <af:panelStretchLayout topHeight="50px" id="psl1">
          <f:facet name="top"/>
          <f:facet name="center">
            <af:panelGroupLayout id="pgl0" layout="horizontal">
              <af:spacer width="10" height="10" id="s1"/>
              <af:outputText value="<div class='badge'>xyz</div>" id="ot1"
                           escape="false"/>
            </af:panelGroupLayout>
            <!-- id="af_one_column_header_stretched"  -->
          </f:facet>
        </af:panelStretchLayout>
      </af:form>
    </af:document>
  </f:view>

And generates

The solution works somehow, if we put an af:button on the page instead of the af:spacer. However, it looks ugly or ‘uncool’ to see the ‘div’ in the af:outputText.

Final Solution

The final solution is to hide the ‘how to build it’ in a declarative component. The use case is a perfect fit for an ADF declarative component. We move the CSS, the button and the outputText in such a component and can use the component in the page to get

<?xml version='1.0' encoding='UTF-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
          xmlns:f="http://java.sun.com/jsf/core"
          xmlns:h="http://java.sun.com/jsf/html"
          xmlns:af="http://xmlns.oracle.com/adf/faces/rich" xmlns:hc="/hahnCompLib"> 
  <jsp:directive.page contentType="text/html;charset=UTF-8"/>
  <f:view>
    <af:document id="d1">
      <af:form id="f1">
        <af:panelStretchLayout topHeight="50px" id="psl1">
          <f:facet name="top"/>
          <f:facet name="center">
            <af:panelGroupLayout layout="scroll" id="pgl2">
              <af:panelGroupLayout id="pgl1">
                <af:inputText label="New Messages" id="it1" value="#{bindings.myNewMessageCount1.inputValue}" autoSubmit="true"/>
                <af:spacer width="10" height="20" id="s1"/>
                <af:panelGroupLayout id="pgl0" partialTriggers="it1">
                  <hc:badgeButton badgeText="#{bindings.myNewMessageCount1.inputValue}" label="New Actions" id="bbcd1"
                                              baListener="#{BadgeButtonBean.onBadgeButton}" baAction="#{BadgeButtonBean.onBadgeButtonAction}"/>
                </af:panelGroupLayout>
              </af:panelGroupLayout>
            </af:panelGroupLayout>
            <!-- id="af_one_column_header_stretched"  -->
          </f:facet>
        </af:panelStretchLayout>
      </af:form>
    </af:document>
  </f:view>
</jsp:root>

The new solution looks clean, as the CSS and odd looking outputText isn’t visible. Here are some images of the running test application.


The input field is used to set the data to be shown on the badge. It can be numbers, text or both. Clicking on the button calls the action listener and the action

package de.hahn.blog.badgebutton;

import javax.faces.event.ActionEvent;
import oracle.adf.share.logging.ADFLogger;

public class BadgeButtonBean {
    private static ADFLogger logger = ADFLogger.createADFLogger(BadgeButtonBean.class);
    public BadgeButtonBean() {
    }

    public void onBadgeButton(ActionEvent actionEvent) {
       logger.info("Test!");
            
    }

    public String onBadgeButtonAction() {
        logger.info("Test5!");
        return null;
    }
}

And this is the log output

Building the Declarative Component

There are many other blogs about how to create a declarative component, so I will not do this in detail. However, I like to point out some things you should keep in mind when you create such a declarative component.

To make the component work, it’s not enough to just add a button and the outputText holding the CSS. We have to make properties available for the action and the actionListener of the button. If we don’t do this, all we get is a button, which is looking good but which can’t be used to start a navigation or to call an actionListener.  The same is true for other properties of the button inside the declarative component. We have to add a property for the button text at least. If you like to enable/disable the badge button, you have to expose an attribute in the declarative component interface to get the value from the outside, your page See later).

When we build the declarative component, we start by adding another ‘ADF View Controller Project’ to the workspace. Declarative components should be developed in a separate project. They are deployed as adfLibrary jar files. We add a new JSF Declarative Component to the project


And fill out a dialog with necessary data. This data can be changed later if needed. Here is a sample


Once we click OK, a jspx page with the selected name will be created alongside a meta-data file holding the information about the tag library.


In the JSPX page, we create the layout of the component. We already know how to add the badge, all we need to do is to put an af:button onto the page and add the code to the badge to it. The resulting declarative component looks like

<?xml version='1.0' encoding='UTF-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"
          xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
  <jsp:directive.page contentType="text/html;charset=UTF-8"/>
  <af:componentDef var="attrs" componentVar="component">
    <af:resource type="css">
        .badge {
        background:             radial-gradient( 5px -9px, circle, white 8%, red 26px );
            background:    -moz-radial-gradient( 5px -9px, circle, white 8%, red 26px );
            background:     -ms-radial-gradient( 5px -9px, circle, white 8%, red 26px );
            background:      -o-radial-gradient( 5px -9px, circle, white 8%, red 26px );
            background: -webkit-radial-gradient( 5px -9px, circle, white 8%, red 26px );
        background-color: red;
        border: 2px solid white;
        border-radius: 12px; /* one half of ( (border * 2) + height + padding ) */
        box-shadow: 1px 1px 1px black;
        color: white;
        font: bold 15px/13px Helvetica, Verdana, Tahoma;
        height: 16px; 
        padding: 4px 3px 0 3px;
        text-align: center;
        min-width: 14px;
        margin: 0px 0px 20px -10px;
        position: relative;
    }
    </af:resource>
    <af:panelGroupLayout id="pgl1" layout="horizontal">
            <af:commandButton text="#{attrs.label}" id="cb1" shortDesc="55" partialSubmit="false" actionListener="#{component.handleBaListener}"
                              action="#{component.handleBaAction}"/>
            <af:outputText value="<div class='badge'>#{attrs.badgeText}</div>" id="ot1" partialTriggers="cb1" escape="false"/>
    </af:panelGroupLayout>
    <af:xmlContent>
      <component xmlns="http://xmlns.oracle.com/adf/faces/rich/component">
        <display-name>badgeButton</display-name>
        <attribute>
          <attribute-name>label</attribute-name>
          <attribute-class>java.lang.String</attribute-class>
          <required>false</required>
        </attribute>
        <attribute>
          <attribute-name>badgeText</attribute-name>
          <attribute-class>java.lang.String</attribute-class>
          <required>true</required>
        </attribute>
        <component-extension>
          <component-tag-namespace>de.hahn.blog.component</component-tag-namespace>
          <component-taglib-uri>/hahnCompLib</component-taglib-uri>
          <method-attribute>
            <attribute-name>
              baListener
            </attribute-name>
            <method-signature>
              void method(javax.faces.event.ActionEvent)
            </method-signature>
            <required>
              false
            </required>
          </method-attribute>
          <method-attribute>
            <attribute-name>
              baAction
            </attribute-name>
            <method-signature>
              java.lang.String method()
            </method-signature>
          </method-attribute>
        </component-extension>
      </component>
    </af:xmlContent>
  </af:componentDef>
</jsp:root>

Select the af:componentDef tag and open the property editor. There you can define metadata like attributes and methods you want to pass to the component from the outside.

And the methods to activate the button

The data you specified in the property editor is added to the af:componentDef  tag

    <af:xmlContent>
      <component xmlns="http://xmlns.oracle.com/adf/faces/rich/component">
        <display-name>badgeButton</display-name>
        <attribute>
          <attribute-name>label</attribute-name>
          <attribute-class>java.lang.String</attribute-class>
          <required>false</required>
        </attribute>
        <attribute>
          <attribute-name>badgeText</attribute-name>
          <attribute-class>java.lang.String</attribute-class>
          <required>true</required>
        </attribute>
        <component-extension>
          <component-tag-namespace>de.hahn.blog.component</component-tag-namespace>
          <component-taglib-uri>/hahnCompLib</component-taglib-uri>
          <method-attribute>
            <attribute-name>
              baListener
            </attribute-name>
            <method-signature>
              void method(javax.faces.event.ActionEvent)
            </method-signature>
            <required>
              false
            </required>
          </method-attribute>
          <method-attribute>
            <attribute-name>
              baAction
            </attribute-name>
            <method-signature>
              java.lang.String method()
            </method-signature>
          </method-attribute>
        </component-extension>
      </component>
    </af:xmlContent>

A special case is the partialTrigger property. You can’t set a partialTrigger from the outside to a component used in the declarative component. However, you can surround the declarative component with another component which has a partialTrigger property and use this outer component to send a ppr. In the code above I surrounded the declarative component with an af:panelGroupLayout which listens for ppr send from the af:inputText with the id “it1”. This partial refresh is needed in the sample to refresh the button with a new value entered in the field.

If you need more attribute to control the behavior of the component, you can add them via the property editor. For this simple test case, the attributes and methods are enough.

The sample application and declarative component is build using JDev 11.1.1.9.0 and can be downloaded from GitHub BlogBadgeButton. The sample doesn’t need a DB.

Timo Hahn