Modern looking Search Input field ADF style

Introduction

In our work with creating complex User-Interfaces in ADF, we often get requests to create a modern version of a search input field.  For example, the following image gives an example of the desired LnF (Look-n-Feel):

modernsearch_1

Notice that the text Search is automatically inserted in the input area, and that the (button) magnifying glass icon, which is responsible for invoking the search, is also within the input area.  When focus is given within the input area, the default Search string is removed, to allow for user entry:

modernsearch_2

In addition, user entered characters that go beyond the visible input area do not enter into the button area:

modernsearch_2a

No doubt that there are plenty of examples out on the web that will provide this functionality and behavior.  However, in the (OOTB) ADF world there is not an implicit (or documented) way to achieve this.  In this blog post, we will detail how we were able to create the example displayed above.

Main Article

Like the examples on the web, which uses HTML input fields (type=text, type=submit), the base model for the ADF components are a (text) af:inputText and a (submit) af:commandButton.  Both of these components are contained within an af:panelGroupLayout.  For example, the following display details the hierarchy structure of the components:

modernsearch_3

The other ADF based tags (i.e. af:clientListener, .etc), will be covered later on…

Although the af:commandButton is technically adjacent to the af:inputText, it appears to be inside of the af:inputText.  This illusion is created through CSS, where borders are strategically aligned and hidden.  In addition, through CSS3, the borders have rounded corners that help transition the view.  The style of both components are controlled from each of the respective style classes.  This enables the CSS to only effect these specific (not global) ADF components.   The following CSS was used in the example:

af|inputText.searchIT af|inputText::content{
    border: solid #ABABAB 1.5px;
    border-right:none;
    height: 17px;
    line-height: 17px;
    width: 100px;
    padding: 2px 4px;
    border-top-left-radius:5px;
    border-bottom-left-radius:5px;
    font-weight: bold;
}

af|commandButton.submitButton:text-only {
    border: solid #ABABAB 1.5px;
    border-left:none;
    height: 23px;
    width: 30px;
    border-top-left-radius:0px;
    border-bottom-left-radius:0px;
    border-top-right-radius:5px;
    border-bottom-right-radius:5px;
    margin-left:-1px;
    background-image: url('/oracle/webcenter/portalapp/shared/Search.png');
    background-repeat: no-repeat;
    background-color:transparent;
    background-position: center;
}

The main part of the illusion is in the af:commandButton. First, the af:commandButton height is sized to match the af:inputText. Then the af:commandButton is positioned right next to the end of the af:inputText by using a negative value in the CSS margin-left property.  The borders are then removed from the start of the af:commandButton and to the end of the af:inputText. Since the magnifying glass icon is the background of the af:commandButton, it appears to be inside of the af:inputText.  That’s it all it takes to create the desired LnF.  The next section will address the clearing of the default text when the search input field has focus.

Remember the af:clientListener and the af:clientAttribute in the component hierarchy display?  The af:clientListener component is responsible for calling a java script method (cleartext) for performing the magical clear function.  The use of the af:clientAttribute, enables a way to pass a value (clearSearch) from a managed bean to the cleartext method.  This value (default = Search…) is used to compare what is currently in the af:inputText.

<af:clientListener type="focus" method="cleartext"/>
<af:clientAttribute name="clearSearch" value="#{pageFlowScope.searchHelper.defaultText}

The value of the compare could also be hard coded, which would then remove the need for the the af:clientAttribute.  However, in my example the embedded “Search…” string also supports localization.  The actual value of the string is retrieved from the application’s resource bundle.

The following is the java script method that was used:

function cleartext(evt){
    var clearVal = evt.getSource();
    var stringToCompare = clearVal.getProperty('clearSearch');
    var input1 = document.getElementById('pt1:searchIT::content');
    if(input1.value == stringToCompare){
        document.getElementById('pt1:searchIT::content').value = "";
    }
}

There is also an af:clientListener on the page, which is responsible for resetting the default search string:

<af:clientListener type="load" method="initsearch"/>

function initsearch(evt){
    var searchVal = evt.getSource();
    var searchLocalizedVal = searchVal.getProperty('defaultSearchVal');
    document.getElementById('pt1:searchIT::content').value = searchLocalizedVal;
}

One question that I always get is instead of using java script to set the default Search string, why didn’t I just set the value property the af:inputText component itself?  I must admit, that this will work if this value is hard coded.  For example:

<af:inputText id="searchIT" styleClass="searchIT"
    value="Search...">
</af:inputText>

However, if I needed to get the Search string value dynamically using expression language (EL) :

<af:inputText id="searchIT" styleClass="searchIT"
  value="#{sessionScope.ChangeLocale.searchtext}">
</af:inputText>

The af:inputText at run time will become a non-editable <span>.  Hopefully, with this blog, I have opened your minds on how to create additional UI designs using standard ADF components.  It’s all in the CSS…

 

Add Your Comment