Wednesday, April 11, 2012

Set Initial Focus component for Oracle ADF Faces

Oracle ADF Faces components provide rich functionality out of the box. One small feature is missing somehow:
a convenient way to set initial focus on input component.

For the inpatient readers: sample workspace to download and start with JDeveloper 11.1.2.1
(11g Release 2) containing a custom tag <adfExt:setInitialFocus/> and a sample usage of it.

<adfExt:setInitialFocus/> features:
  • Drag and Drop usage  from Control Palette in JDeveloper: ADF Extensions -> Set Initial Focus
  • Drop on editable components, like inputText etc.
  • Handles multiple usage of the tag - initial focus is placed on first input component rendered in a view
  • Handles submit on a same view - the focus should stay where it is
  • Handles  properties readOnly, disabled, visible of input component.
    Initial focus doesn't provide much value in case an input component is not visible or read only.
  • Works with regions and therefore with components reused multiple times
The implementation takes advantage of JSF2 (JDeveloper R2)  features like facelets tag library, @FacesBehavior annotation and Behavior API.

NOTE: The principles of implementation can be successfully applied in a previous versions (11g R1 with JSF 1.x) , feel free to take a code and adapt it to your needs.

Steps to get the tag into your web project and use it in JDeveloper Component Palette (directory structure of a sample web project is assumed):
  1. Copy the file SetInitialFocusBehavior.java  (implementation of the tag) into your project.
  2. Copy the file adfExt.taglib.xml (facelets tag library with a definition of it) into WEB-INF\resources directory 
  3. Add the following context parameter  into web.xml:

Check if tag library is present in JDeveloper Project Properties:

Once configured, a new library ADF Extensions should be available in JDeveloper Component Palette:

There is one component in a library:


Drag and Drop it on some input component, like af:inputText:



Once the page is rendered, a cursor should be placed on that component:

Lets take a closer look to the use case of "Set Initial Focus" component.


User experience

A user opens an input form (built with ADF Faces) to enter some data. The cursor is placed on first editable field, so he can proceed using a keyboard instead of grabbing a mouse first.  After initial focus is set, the user takes a control on cursor position. Once the user took a control and started to navigate ( using TAB or arrow keys), he can finish his work and submit a data entered.


Note: the first input field might not always be the case  - a developer might choose the most suitable component to set initial focus on, based on specific requirements. 


Developer experience 

A software developer might expect to mark a component as a candidate for the initial focus with almost no effort:
  • set a property on appropriate component (initialFocus = true)
    or
  • Drag a control, like "Set Initial Focus" from a component palette in JDeveloper and drop it on the input component of a page.

Implementation challenges

ADF is designed for reusability, especially the bounded task flows facilitate that. One of the challenges covered in this blog post  from Frank Nimphius is related to the naming containers and the naming path: it is not always a case to know it in advance. It results in difficulties to locate a component in Javascript.
Reusable components are (and shouldn't be) not aware about a usage context of them. The simplest way to set a focus is on the other hand to call a Javascript function focus() on component once it has been rendered. We need to get an instance of component in Javascript, therefore need a client Id in order to find it.

ADF Faces provide a build-in AJAX functionality to reload  parts of a page. The implementation using Javascript has to take care about it. In case of partial refresh, a user is still on a same page and doesn't expect the cursor to jump away (see the blog post from Edwin Biemond and the first comment on it).

Addressing the challenges

The implementation of <adfExt:setInitialFocus/> tag is aimed to address the issues described in order to provide an experience  users and developers expect. Some new features provided by JSF2 are used.

One of the features used is Behaviour. See the blog post Das Behavior API with a description of it (oups - it is german link, sorry -  I took a notice about a language only reviewing a post. I leave the link  - modern browsers offer built-in translation, so it helps to google for additional resources in case of interest). My expectation on ClientBehaviour was to get an access to the clientId of component in java code. I'm not sure, if its by design, but one might expect that in case a ClientBehaviour tag is attached, the ADF Faces component renders also a client-peer of it. In that case, there is no need to set a "client Component=true" every time you use initial focus tag. The implementation and my simple tests met the expectation. Drop me a line in case you are familiar with internals of ADF Faces in relation with  ClientBehavior.

Facelets in JSF2 allows to build a tag libraries with ease, so it was selected to provide initial focus functionality for developers as custom tag.

The sources of sample project and implementation are available on public Github repository. In case someone might want to extend, rewrite or fix some issue - it is aimed to facilitate that.

It would be nice to get a feedback on usage of the tag - does it behave in your environment like expected?

To finish the post, a snippet of the source provides the rest of implementation details:

7 comments:

  1. This is a grate post Donatas.

    As you mentioned in this post "NOTE: The principles of implementation can be successfully applied in a previous versions (11g R1 with JSF 1.x) , feel free to take a code and adapt it to your needs."
    I have such requirement so i also want to implement the same tag lib kind of concept but i am using Jdev 11.1.1.6...
    it would be great if you can give me some direction to implement the same kind tag lib in Jdev11.1.1.6.
    Thanks in advance.
    ~Sandy

    ReplyDelete
  2. Hi Sandy, the functionality which relies on facelets and behavior is JSF2 specific.
    The alternative JSF 1.2 based implementation to get initial focus working with JDeveloper R1 (also 11.1.1.6) might look like this:

    1) Create managed bean java class based on the code of
    public class SetInitialFocusBehavior like this: rename the class to SetInitialFocusBehaviorBean, remove an interface, annotation and all JSF2 specific imports and types to get the class compiled.

    2)...
    // Get the instance of Faces component for the tag
    UIComponent component = clientBehaviorContext.getComponent();
    ...

    clientBehaviorContext ist not available in JSF 1.2 , but weed need to get a client Id somehow - so create one property in a bean class, for example:
    private UIComponent focusComponent; + getter und setter.

    Declare your bean as a managed bean , scope backingBean , name it for example SetInitialFocusBehaviorBean

    In order to put initial focus on some component, for example af:inputInputText:

    1) add 2 properties to af:inputText tag like this:



    2) At the bottom of your jsf page put af:outputText tag like this:



    This invisible output is used just to call a method getScript , which renders a javaScript to set initial focus on.

    Regards, Donatas



    ReplyDelete
  3. Oups.. html is not rendered in comments - here is a patch for my previous comment - now with escaped html:)

    ....

    In order to put initial focus on some component, for example af:inputInputText:

    1) add 2 properties to af:inputText tag like this:

    <af:inputText ...
    binding="#{SetInitialFocusBehaviorBean.focusComponent}"
    clientComponent="true"/>

    2) At the bottom of your jsf page put af:outputText tag like this:

    <af:outputText value="#{SetInitialFocusBehaviorBean.script}" visible="false"/>

    ...

    ReplyDelete
  4. 1)I added this setInitialtag onto an already created xml where I have another customade tag, but is not recognizing, it says is not expected, I added one to your project also, but still same error, am I missing a step?

    2)My components are not shown in the pallete, I made sure to check the "Show tag library in pallete" checkbox

    ReplyDelete
  5. Hi, Diego Rafael Flores

    check if you are using a correct JDeveloper R2 - the techique described in this blog post works starting from JDeveloper R2 ( 11.1.2.xxx ) due to a usage of JSF2 functionality.

    In case you are using JDeveloper R1 ( 11.1.1.xx) please refer to the blog post of Wilfred van der Deijl:

    http://www.redheap.com/2013/02/custom-adf-faces-client-behavior-jsp-tag.html

    Regards, Donatas

    ReplyDelete
  6. Hi,
    I want to move the behavior in a separate library and put all the JS in a separate file inside the library. What do I need to do in order to make the browser to load the JS file at runtime?
    Thank you.

    ReplyDelete
  7. Hi Bogdan,

    it is already packaged as a "library". There is no separate javascript needed as javascript gets rendered dynamically by SetInitialFocusBehavior.java class.

    Regards, Donatas

    ReplyDelete