ADF 11g – Use the Change(d) Indicator to make AJAX originated value refresh explicit to the user

1

 

ADF 11g Rich Client Components have a facility called the Change(d) Indicator. The changed attribute is specified using a Boolean value or expression (typically the latter). When the expression evaluates to true, a visual indication is rendered on the component that signifies to the user that the item’s value has changed. While we can use this indicator in any way we see fit, a logical use case would be to highlight with this indicator any field that has been updated as a result of a partial page request/refresh – the AJAX style immediate update of portion of the page as a response to action by the user.

For example: when the temperature is entered in the Temperature Fahrenheit item (or changed using the Spin Controls), an auto submit is performed that in turn leads to an update of the Temperature Celsius item. Because of the changed attribute, the user can clearly identify the items that were automagically changed. Note: In this case we have used a custom skin that specifies another icon for the changed indicator (you will see the default icon later on in this article).

Note – displaying the changed indicator is achieved in a very simple way: just add the changed attribute to the component and set its value to true:

<af:inputText label="Some Field" changed="true"/>

Initially I assumed that ADF would somehow keep track of changed components itself, by comparing submitted values and stored values or registering special valueChangeListeners. However, it seems that we are completely in charge of setting the appropriate value of the changed attribute.

In this example, I have done the following:

1. Create a class TemperatureConverter – a bean with two properties: temperatureCelsius and temperatureFahrenheit. When the setter for either property is called with a new value, the other property is re-calculated. The changed flag for that other property is set (in the changedFlagsMap).

2. Configure a managed bean temperatureManager – session scope – based on this class

3. Also configure a managed bean changedFlagsMap – request scope – based on java.util.HashMap; this bean will contain entries for all fields that are currently in a changed state.

4. Create a JSF page with two items based on the two bean properties Both items have autoSubmit set to true (they send their new value to the managed bean as soon as the user applies the change). Both items have the partialTriggers property set to refresh after a PPR initiated by the other item.

5. Both items have an Expression in their changed attribute that refers to the changedFlagsMap.

The result is:

 

Here you see the default change indicator. I have entered 25 into the Temperature Celsius field. The autoSubmit caused this value to be sent to the managed bean on the server that recalculated the Fahrenheit temperature and set the flag in the map with all changed flags. The partialTriggers on the Temperature Fahrenheit field that reference the Celsius field causes the item to be refreshed and the reference in the changed attribute to the changed-flags-map makes the changed indicator display.

Note: when you press the Submit Page button, the changed indicator will disappear as the Changed Flags Map is a request scope bean and therefore reset with this new request; no flags are set as no (new) changes have occurred.

The steps in detail (you can also download the JDeveloper 11g Application to try it for yourself):

1. Create a class TemperatureConverter

public class TemperatureConverter {

    private float temperatureCelsius;
    private float temperatureFahrenheit;

    public TemperatureConverter() {
    }

    public void setTemperatureCelsius(float temperatureCelsius) {
        this.temperatureCelsius = temperatureCelsius;
        this.temperatureFahrenheit =
                (float)(9.0 * (temperatureCelsius) / 5.0 + 32.0);
        registerChange("tf");
    }

    public float getTemperatureCelsius() {
        return temperatureCelsius;
    }


    public void setTemperatureFahrenheit(float temperatureFahrenheit) {

        this.temperatureFahrenheit = temperatureFahrenheit;
        this.temperatureCelsius =
                (float)(5.0 * (temperatureFahrenheit - 32.0) / 9.0);
        registerChange("tc");
    }

    public float getTemperatureFahrenheit() {
        return temperatureFahrenheit;
    }

    private void registerChange(String flagToSet) {
        FacesContext fctx = FacesContext.getCurrentInstance();
        ELContext elctx = fctx.getELContext();
        ValueExpression valExpr =
            fctx.getApplication().getExpressionFactory().createValueExpression(elctx,
                                                                               "#{changedFlagsMap}",
                                                                               Map.class);
        Map flags = (Map)valExpr.getValue(elctx);
        flags.put(flagToSet, Boolean.TRUE);
    }
}

2. Configure a managed bean temperatureManager

  <managed-bean>
    <managed-bean-name>temperatureManager</managed-bean-name>
    <managed-bean-class>nl.amis.view.TemperatureConverter</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
  </managed-bean>

3. Also configure a managed bean changedFlagsMap

  <managed-bean>
    <managed-bean-name>changedFlagsMap</managed-bean-name>
    <managed-bean-class>java.util.HashMap</managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
  </managed-bean>

4. Create a JSF page with two items based on the two bean properties

5. Both items have an Expression in their changed attribute that refers to the changedFlagsMap.

        <af:panelHeader text="Auto-submit on both items leads to AJAX style update">
          <af:panelFormLayout>
            <f:facet name="footer"/>
            <af:inputNumberSpinbox label="Temperature Fahrenheit" id="tf"
                                   value="#{temperatureManager.temperatureFahrenheit}"
                                   autoSubmit="true"
                                   <strong>changed=&quot;#{changedFlagsMap['tf']}&quot;</strong>
                                   partialTriggers=&quot;tc&quot;/&gt;
            &lt;af:inputNumberSpinbox label=&quot;Temperature Celsius&quot;
                                   value=&quot;#{temperatureManager.temperatureCelsius}&quot;
                                   id=&quot;tc&quot; <strong>changed=&quot;#{changedFlagsMap['tc']}&quot;</strong>
                                   autoSubmit=&quot;true&quot; partialTriggers=&quot;tf&quot;/&gt;
          &lt;/af:panelFormLayout&gt;
        &lt;/af:panelHeader&gt;

Step Two – Customizing the Changed Icon using a Skin

If you would like to display a different icon as changed indicator, you can achieve that using a custom skin. This requires you to go through a few simple steps. Steps that you should try to through as soon as possible – once you have the hang of creating your own skin, you can really start to take advantage of all the options skinning offers you.

The steps are:

1. Create WEB-INF\trinidad_skins.xml

&lt;?xml version=&quot;1.0&quot; encoding=&quot;ISO-8859-1&quot;?&gt;
&lt;skins xmlns=&quot;http://myfaces.apache.org/trinidad/skin&quot;&gt;
    &lt;skin&gt;
        &lt;id&gt;amis.desktop&lt;/id&gt;
        &lt;family&gt;amis&lt;/family&gt;
        &lt;render-kit-id&gt;org.apache.myfaces.trinidad.desktop&lt;/render-kit-id&gt;
        &lt;style-sheet-name&gt;skins/amis.css&lt;/style-sheet-name&gt;
        &lt;extends&gt;blafplus-rich.desktop&lt;/extends&gt;
    &lt;/skin&gt;
&lt;/skins&gt;

Here I specify that my own skin is in a file amis.css in the skins subdirectory under the public_html folder. This skin extends the standard rich faces skin (blafplus).

2. Modify WEB-INF\trinidad-config.xml

&lt;?xml version=&quot;1.0&quot; encoding=&quot;windows-1252&quot;?&gt;
&lt;trinidad-config xmlns=&quot;http://myfaces.apache.org/trinidad/config&quot;&gt;
  &lt;skin-family&gt;amis&lt;/skin-family&gt;
&lt;/trinidad-config&gt;

This specifies that the amis skin (configured in trinidad-skins.xml)

3. Create stylesheet (CSS document) under public_html

/* Redefine tha icon shown for items whose changed attribute evaluates to true */
.AFChangedIcon:alias {content:url('/images/changedindicator.jpg');width:15px}

The AFChangedIcon alias allows us to specify the ChangedIcon across the application. This alias is inherited by all components that have their own changedIcon selector, such as af|inputColor::changed-icon and af|inputComboboxListOfValues::changed-icon.

4. Save an approriate changed indicator icon to the Web Application

And that is it.

When you next run the application, the new skin definition is applied and the newly specified icon is displayed instead of the standard changed indicator icon.

 

Resources

Download JDeveloper 11g Application: changedindicator.zip .

Skinning Documentation:  http://jdevadf.oracle.com/adf-richclient-demo/docs/skinsdoc/skin-selectors.html

Frank Nimphius’ Example on creating a custom skin for manipulating the ADF 11g Rich Splash Screen.

Share.

About Author

Lucas Jellema, active in IT (and with Oracle) since 1994. Oracle ACE Director for Fusion Middleware. Consultant, trainer and instructor on diverse areas including Oracle Database (SQL & PLSQL), Service Oriented Architecture, BPM, ADF, Java in various shapes and forms and many other things. Author of the Oracle Press book: Oracle SOA Suite 11g Handbook. Frequent presenter on conferences such as JavaOne, Oracle OpenWorld, ODTUG Kaleidoscope, Devoxx and OBUG. Presenter for Oracle University Celebrity specials.

1 Comment

  1. Maybe you could expose a dynamic view row attribute in a generic way of type Map which returned the boolean result of ViewRowImpl.isAttributeChanged(String attrName) so an ADFBC-based app could offer up a convenient expression to automatically flag which values were changed in the UI. Just a quick thought after reading this…