Thursday Sep 05, 2013

JDeveloper 11g R2 and 12c: How-to "virtually" press a button on page load

A frequent asked question is about how to press a command button upon load of a page or view. If you are on JDeveloper 11g R2 or JDeveloper 12c, then the solution to this programming challenge is the use of JavaServer Faces (JSF) 2 system events (If you are on JDeveloper 11g R1 then you are on JSF 1.2 and this JSF functionality doesn't exist).

System events are phase listeners that you  associate with a component instead of a view or page as a whole. In the use case mentioned above, the system event would be added to the command button you want to pres on initial page load, e.g.

<af:commandButton text="Press on Page Load" id="cb1"
       actionListener="#{viewScope.OnLoadHandler.onButtonPressed}"
       partialSubmit="true">
   <f:event listener="#{viewScope.OnLoadHandler.pressButton}" type="postAddToView"/>
</af:commandButton> 

 As you can see the f:event tag is added as a child tag to the af:commandButton (You find the event tag in the JDeveloper component palette under JSF -> Core) pointing to a listener defined in a managed bean that listens for the "postAddToView" phase. The managed bean is configured in viewScope to ensure the button in the use case is only "virtually pressed" once and not upon PPR of the button area. Using a managed bean in view scope, a flag can be kept that determines whether or not the button has been invoked already.

The event handler uses JavaScript to "virtually press" the button after the page or view is loaded:

...

boolean initialPageLoad = false;

...

public void pressButton(ComponentSystemEvent componentSystemEvent) {
 //only execute button once and not on PPR is used
 if (initialPageLoad == false) {
  //get access to the command button to execute "click"
  RichCommandButton rc = (RichCommandButton)componentSystemEvent.getComponent();
  String clientId = rc.getClientId();

  //compose the JavaSCript to invvoke from the server. The button client ID allows 
  //us to locate the button even if it is saved in a naming container (e.g. a region) 
  //so the absolute component access will do
  String javaScript = "var button = AdfPage.PAGE.findComponentByAbsoluteId('"+clientId+"');
                      AdfActionEvent.queue(button,true)";
    
  //invoke the JS
  FacesContext facesContext = FacesContext.getCurrentInstance();
  ExtendedRenderKitService service = Service.getRenderKitService(facesContext, 
                                              ExtendedRenderKitService.class);
  service.addScript(facesContext, javaScript);

  //change flag   
  initialPageLoad = true;
 }
}

 This way, whatever is configured for the command button press is executed upon page / view load.

A valid question is: "why don't you execute the Java method associated with a command button directly instead of  using JavaScript?" The answer to this is that using JavaScript, associated behavior tags (e.g. for printable behavior or to show a popup) are executed too as it "mimic" a user behavior.

Of course, if there is no client side behavior associated with a command button, its better to not use JavaScript but call the Java method associated with the button directly.

 Frank




Tuesday Jun 04, 2013

How-to highlight input field content when accessed from auto-tab

A question on the OTN forum has been how to highlight the content of an input field when navigation to it occurs using the auto tab functionality. As a reminder, the "autotab" property of an input field, if set to "true", tabs out of the field that you currently edit when the maximum length of the field entry is reached.

The problem reported on OTN for JDeveloper 11.1.2.4 (and I am sure its the same on other versions of JDeveloper) is that manual tabbing into a next field will highlight the fields content (value) whereas autotab doesn't. To enforce consistent highlighting behavior, you can use JavaScript as shown in the page sample below:

<f:view xmlns:f="http://java.sun.com/jsf/core" xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
    <af:document title="AutoTab.jsf" id="d1">
    <af:resource type="javascript">
      function onFocusHandler(inputEvent){
         var textfield = 
             AdfAgent.AGENT.getElementById(inputEvent.getSource().getId()+'::content');
         textfield.select();
     }
    </af:resource>
      <af:form id="f1">
        <af:panelFormLayout id="pfl1">
            <f:facet name="footer"/>
             <af:inputText label="First Name" id="it1" 
                           value="#{viewScope.fieldValues.firstName}" 
                           maximumLength="5" autoTab="true"/>
             <af:inputText label="Last Name" id="it2" 
                           value="#{viewScope.fieldValues.lastname}">
                <af:clientListener method="onFocusHandler" type="focus"/>
             </af:inputText>
          </af:panelFormLayout>
       </af:form>
   </af:document>
</f:view>

The af:clientListener is attached to the input field for which you want to show content highlighting on autotab. Attach the same client listener definition to  all field that should have the content highlighting. Instead of adding the JavaScript to the page, you want to create an external JS library file and link it from the page as the JS code above is reusable.

Note that the use of "'::content" in the JavaScript uses knowledge about the way component IDs are rendered at runtime in ADF Faces. If component rendering changes in a future release (you never know) then your code will break at this point and needs to be corrected. So heads-up on this risk (though should be a small one). To the time of writing this lines there is no other API (option) available to get to the HTML field handle in ADF Faces.

Also note the use of AdfAgent.AGENT.getElementById, which is a performant wrapper API in ADF Faces around the document.findElementById function of the Browser DOM. As a rule of thumb you should always work with ADF Faces JS APIs and don't directly reach out to the DOM yourself.

Monday Dec 17, 2012

Accessing ADF Faces components that are read-only from JavaScript

Almost as a note to myself and to justify the time I spent on analyzing aproblem, a quick note on what to watch out for when working trying to access read-only ADF Faces components from JavaScript. 

Those who tried JavaScript in ADF Faces probably know that you need to ensure the ADF Faces component  is represented by a JavaScript object on the client. You do this either implicitly by adding an af:clientListener component (in case you want to listen for a component event) or explicitly by setting the ADF Faces component clientComponent property to true.

For the use case I looked at in JDeveloper 11g R1 (11.1.1.7) I needed to make an output text component clickable to call a JavaScript function in response. Though I added the af:clientListener tag to the component I recognized that it also needed the clientComponent property set to true.

Though I remember this as not being required in 11.1.1.6, I like the new behavior as it helps preventing read-only components from firing client side events unless you tell it to do so by setting the clientComponent property to true.

Note: As the time of writing, JDeveloper 11.1.1.7 is not publicly available and I put the note in this blog as a reminder in case you ever hit a similar challenge so you know what to do.


Wednesday Mar 28, 2012

Gotcha when using JavaScript in ADF Regions

You use the ADF Faces af:resource tag to add or reference JavaScript on a page. However, adding the af:resource tag to a page fragment my not produce the desired result if the script is added as shown below

<?xml version='1.0' encoding='UTF-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
          xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
 <af:resource type="javascript">
  function yourMethod(evt){ ... }
</af:resource>

Adding scripts to a page fragment like this will see the script added for the first page fragment loaded by an ADF region but not for any subsequent fragment navigated to within the context of task flow navigation. The cause of this problem is caching as the af:resource tag is a JSP element and not a lazy loaded JSF component, which makes it a candidate for caching.

To solve the problem, move the af:resource tag into a container component like af:panelFormLayout so the script is added when the component is instantiated and added to the page. 

<?xml version='1.0' encoding='UTF-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
          xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
<af:panelFormLayout>
 <af:resource type="javascript">
    function yourMethod(evt){ ... }
 </af:resource>
</af:panelFormLayout> 

Magically this then works and prevents browser caching of the script when using page fragments.

Thursday Mar 08, 2012

Using af:serverListener as a JS client-server proxy

Despite of ADF Faces having a client side JavaScript architecture, JavaScript rule #1 in ADF Faces is to use JavaScript as a fallback option only for development use cases in which there is no native solution to a problem. A built-in feature of the ADF Faces JavaScript client architecture is security that disallows certain component properties like disabled and readOnly to be changed from JavaScript.

To quote the JavaScript doc for the AdfRichInputText object:

http://docs.oracle.com/cd/E12839_01/apirefs.1111/e12046/oracle/adf/view/js/component/rich/input/AdfRichInputText.html#getReadOnly__

public Boolean getReadOnly()

Get function for attribute for 'readOnly'. This attribute is secured. You may get it, but you may not set it. Any changes to this attribute will not be transmitted to the server.

Note: The disabled property can be enabled for JavaScript modification by setting the component unsecure property as explained in the component documentation, which however I don't recommend you to do. If you need to open protected features for modification, do so in a way that you control, for example in that you can check a user permission to do so.

There might be use cases in which you need to change a protected property from JavaScript, and here is how this can and should be done:

The ADF Faces af:serverListener component allows developers to invoke server side Java from JavaScript and thus can be used as a proxy to bypass applied client side security.

For example, to be able to change the readOnly property on an af:inputText component, you define the text field as shown below

<af:inputText label="Label 2"
              id="it2" 
              clientComponent="true"                      
              value="#{SetInputTextFieldReadOnly.textValue}">
              <af:serverListener type="serverAction"
                                 method="#{SetInputTextFieldReadOnly.actionEvent}"/>
</af:inputText>

The clientComponent property need to be set to true on the component to ensure a JavaScript object is created on the client architecture. With no clientComponent set to true and no af:clientListener attached, the text field component is not accessible from JavaScript at all.

The af:serverListener needs to be configured on the component that is passed as a component reference to the server. In the sample, the component reference that is sent to the server is the input text field that you want to switch from updateable to read-only. If the integration is from a non ADF Faces component, like an Applet, you would configure the af:serverListener on the af:document component. If this is the case, you may not make use of the component reference on the server and instead pass additional payload arguments.

In the Oracle JDeveloper 11.1.1.6 sample to this article the switch between read-only and updateable is simulated by a command button you can press. The sample can be dowloaded from here.

In a realistic use case, this switch would be triggered from a non ADF Faces component like Applet or another technology you integrate in your ADF Faces page.

Note: The use of a command button in the sample is only for demonstration purpose. If the trigger was a command button then no JavaScript would be needed at all and JavaScript rule #1 would apply

On the server, a managed bean method is invoked by the client JavaScript call shown below:

<af:resource type="javascript">
  function setUpdateable(actionEvent){
     actionEvent.cancel();
     var commandButton = actionEvent.getSource();
     var textFieldId = commandButton.getProperty('componentId');
     invokeServerAction(textFieldId,actionEvent,'true');
  }
       
  function setReadOnly(actionEvent){      
    actionEvent.cancel();
    var commandButton = actionEvent.getSource();
    var textFieldId = commandButton.getProperty('componentId');
    invokeServerAction(textFieldId,actionEvent,'false');
  }   
  function invokeServerAction(compId,evt,isUpdateable){
    var textField = evt.getSource().findComponent(compId);            
    AdfCustomEvent.queue(
         textField,"serverAction",
         {updateable: isUpdateable},false);                              
  }
</af:resource>

Note: payload arguments are surrounded by curly braces. If you want to pass multiple arguments then you use a comma separated list of key1:value1, key2:value2 …pairs.

The JavaScript is taken from the sample provided for this tip and – as mentioned – is invoked from a command button. The command button has a custom property (af:clientAttribute) added that tells the script about the text component Id to modify. The component and the desired switch state "readOnly" or "updateable" is passed to the managed bean method:

 /**
  * Proxy the client request
  * @param clientEvent The client event gives you access to the 
  * component that raises the event as well as to the paraneters 
  * passed from the client to the server 
 */
 public void actionEvent(ClientEvent clientEvent) {
  /*
   * This would be a good place to check an ADF Security resource
   * permission if the user is allowed to perform the change on the 
   * attribute. There is no security in JavaScript, but using server 
   * side JAAS protects you from unauthorized changes
   */
   RichInputText inputText =  (RichInputText) clientEvent.getComponent();
   String updateable = (String) clientEvent.getParameters().get("updateable");
   inputText.setReadOnly(!Boolean.parseBoolean(updateable));
   AdfFacesContext adfFacesContext = AdfFacesContext.getCurrentInstance();
   adfFacesContext.addPartialTarget(inputText);
 }

Sample Download

https://blogs.oracle.com/jdevotnharvest/resource/SetInputTextToReadOnlyJS.zip

Monday Jan 30, 2012

Disclose panelBox with a double-click on the header

No long post today, just some sample code showing the bit of JavaScript you need to add to a panel box to toggle it open/close on a double click.

<f:view xmlns:f="http://java.sun.com/jsf/core" xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
  <af:document title="PanelBoxTester.jsf" id="d1">
    <af:resource type="javascript">
       function togglePanelBox(mouseEvent){
          var panelBox = mouseEvent.getSource();
          panelBox.broadcast(new AdfDisclosureEvent(panelBox, !panelBox.getDisclosed()));
       }
     </af:resource>
        <af:form id="f1">
          <af:panelBox text="PanelBox1" id="pb1">
              <f:facet name="toolbar"/>
                <af:clientListener method="togglePanelBox" type="dblClick"/>
            </af:panelBox>
        </af:form>
    </af:document>
</f:view>

        
    

Friday Oct 14, 2011

How to programmatically set focus on an input component

The af:document component has a property InitialFocusId that allows you to define the Id of an input component that should take focus when a page initially loads. But how do you set the component focus for pages that are already rendered? A use case for this is a partial submit that executes logic on the server, after which the cursor focus in the UI needs to be set to a specific input component of a page or page fragment. The solution to this use case is JavaScript that is generated on the server and executed on the client.

The simplified sample I wrote contains of three input text fields and three command buttons. Initially, using the af:document InitialFocusId property, the cursor focus is set to the first input text field.

The command button send a partial submit to a managed bean on the server. The managed bean reads the component Id of the input text field to put the focus to from a client attribute that is defined on each command button. Using the MyFaces Trinidad ExtendedRenderKitService class, the managed bean sends a JavaScript call to the client to change the current component focus.

Important: By default, ADF Faces does not render all its user interface components as JavaScript objects. It only renders those components as JavaScript objects that have behavior, like a table that can have its columns moved. Input text components, for example, are rendered in HTML only, which means that the ADF client side JavaScript framework will not find a handle to the component unless you tell ADF Faces to create one. For this, you set the ClientComponent property of the input text fields to true. This also is required for the af:document InitialFocusId property to work.

Below is the managed bean code that handles the command button action and that composes and executes the JavaScript to set the client side UI focus.

import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import oracle.adf.view.rich.component.rich.input.RichInputText;
import oracle.adf.view.rich.component.rich.nav.RichCommandButton;
import org.apache.myfaces.trinidad.render.ExtendedRenderKitService;
import org.apache.myfaces.trinidad.util.Service;
public
class FocusBean {
  public FocusBean() {
  }

 public String onSetFocus(ActionEvent event) {
   RichCommandButton rcb = (RichCommandButton)event.getSource();
   String focusOn = (String)rcb.getAttributes().get("focusField");
   FacesContext fctx = FacesContext.getCurrentInstance();
   UIViewRoot viewRoot = fctx.getViewRoot();
  //search can be improved to include naming containers
  RichInputText rit = RichInputText)viewRoot.findComponent(focusOn);
 if (rit != null) {
   String clientId = rit.getClientId(fctx); 
   //compose JavaScript to be executed on the client 
   StringBuilder script = new StringBuilder();
   //use client id to ensure component is found if located in
   //naming container  
   script.append("var textInput = ");
   script.append("AdfPage.PAGE.findComponentByAbsoluteId");
   script.append ("('"+clientId+"');");
 
   script.append("if(textInput != null){"); 
   script.append("textInput.focus();");
   script.append("}");
   //invoke JavaScript
   writeJavaScriptToClient(script.toString());
  }  
 }
  //generic, reusable helper method to call JavaScript on a client
  private void writeJavaScriptToClient(String script) {
    FacesContext fctx = FacesContext.getCurrentInstance();
    ExtendedRenderKitService erks = null;
    erks = Service.getRenderKitService(
                 fctx, ExtendedRenderKitService.class);
    erks.addScript(fctx, script);
  }
}

 As mentioned, the command buttons in the sample have a client attribute attached that I use to determine which input component should receive focus. The managed bean calls the client attribute here:

RichCommandButton rcb = (RichCommandButton)event.getSource();
String focusOn = (String)rcb.getAttributes().get("focusField");

 In the page definition, the client attribute is defined as

<af:commandButton text="Focus Field 2" id="commandButton1"
                   partialSubmit="true"
                   actionListener="#{FocusBean.onSetFocus}">
  <af:clientAttribute name="focusField" value="it2"/>
</af:commandButton>
<af:commandButton text="Focus Field 3" id="cb1"
                  partialSubmit="true"
 actionListener="#{FocusBean.onSetFocus}">
  <af:clientAttribute name="focusField" value="it3"/>
</af:commandButton>

In your custom implementation of this use case, you for sure will have your own way of determining the next focus target. The server side code however doesn't change much.

You can DOWNLOAD the sample workspace from here: http://blogs.oracle.com/jdevotnharvest/resource/SetFocusToField.zip


Friday Jul 08, 2011

How-to access UI component that queued a custom client event

In ADF Faces, to invoke a server side method in a managed bean, you use the af:serverListener tag. The af:serverListener tag is added as a child to the component that owns the event and called from JavaScript in a call to AdfCustomEvent.queue( … )

In this example, the af:serverListener is added to a table to notify a manage bean method about a double-click action.

<af:table ...>
 <af:column> ... </af:column>
 ...
 <af:clientListener method="handleTableDoubleClick"
                   type="dblClick"/>
<af:serverListener type="TableDoubleClickEvent"                          
                   method="#{myBean.handleTableDoubleClick}"/>
</af:table> 

The JavaScript function that is called by the af:clientListener is shown next.

function handleTableDoubleClick(evt){   
  var table = evt.getSource();
  AdfCustomEvent.queue(table, "TableDoubleClickEvent",{}, true);         
  evt.cancel();
}

The first argument in the call to AdfCustomEvent.queue represents the event owner, the table component. This information is passed to the managed bean method, which has the following signature.

public void handleTableDoubleClick(ClientEvent ce){
  RichTable richTable = (RichTable)ce.getComponent(); 
  //... work with rich table component
}

As you can see, there is no need to look up the event owning component by searching the JSF UIViewRoot with or without help of JSFUtils.

Thursday Jul 07, 2011

New client behavior tag - af:checkUncommittedDataBehavior

In Oracle JDeveloper 11.1.2, a new client behavior tag af:checkUncommittedDataBehavior is provided to check for uncommitted data when navigating away from a page using a command button that has its immediate property set to true. The tag can be applied as a child of any command component, like

  • af:commandButton
  • af:commandLink
  • af:commandMenuItem
  • af:commandToolbarButton

http://download.oracle.com/docs/cd/E16162_01/apirefs.1112/e17491/tagdoc/af_checkUncommittedDataBehavior.html

For the client behavior to work, you must set the document tag's uncommittedDataWarning attribute to on.

Just as a reminder: Client behavior tags in ADF Faces execute on the client side. They are there to save you from writing JavaScript, thus abstracting you from the complexity of the underlying client side APIs.

Tuesday Nov 30, 2010

Get social security numbers right

A common development use case is to guide users when working with input fields that require a specific input format. For example, credit card and social security number fields use character delimiters that you may want to enforce on a field. The following sample uses JavaScript to add a defined delimiter character according to a defined character.

The American social security pattern is defined as xxx-xx-xxxx. Users that type 123456789 should have the input automatically corrected to 123-45-6789 while they type. Also, the field should be protected from character input and input length larger than the provided pattern.

<af:inputText label="Social Security Number" id="it1"
rendered="true">
<af:clientListener
method="handleNumberFormatConversion('xxx-xx-xxxx','-')"
type="keyDown"/>
</af:inputText>

With the above configuration, the handleNumberFormatConversion method is called for each key stroke in the input field. Additional arguments provided to the function are the input pattern and the character delimiter.

The JavaScript code that is addressed by the clientListener on the InputText is shown below:

// JavaScript function that applies a specific format
// to numeric input. The pattern argument defines the
// input mask, e.g. xxx-xx-xxxx. The delimiter defines
// the delimiter character to add to the user input based
// on the pattern

function handleNumberFormatConversion(pattern, delimiter){
return function(evt){
var inputField = evt.getCurrentTarget();
var keyPressed = evt.getKeyCode();
var oldValue = inputField.getSubmittedValue();
//keycode 48-57 are keys 0-9
//keycode 96-105 are numbpad keys 0-9

var validKeys = new Array(48,49,50,51,52,53,54,55,
56,57,96,97,98,99,100,
                                 101,102,103,104,105,
AdfKeyStroke.ARROWRIGHT_KEY,
AdfKeyStroke.ARROWLEFT_KEY,
AdfKeyStroke.BACKSPACE_KEY,
AdfKeyStroke.DELETE_KEY,
AdfKeyStroke.END_KEY,
AdfKeyStroke.ESC_KEY,
AdfKeyStroke.TAB_KEY);

var numberKeys = new Array(48,49,50,51,52,53,54,55,
56,57,96,97,98,99,100,
101,102,103,104,105);

var isValidKey = false;
for (var i=0; i < validKeys.length; ++i){
if (validKeys[i] == keyPressed) {
isValidKey = true;
break;
}
}
if(isValidKey){
//key is valid, ensure formatting is correct
var isNumberKey = false;
for (var n=0; n < numberKeys.length; ++n){
if(numberKeys[n] == keyPressed){
isNumberKey = true;
break;
}
}
if(isNumberKey){
//if the user provided enough data, cancel
//the input
var formatLength = pattern.length;
if(formatLength == oldValue.length){
inputField.setValue(oldValue);
evt.cancel();
}
//more values allowed. Check if delimiter needs to
           //be set
else{
//if the date format has a delimiter as the next
//character, add it
if(pattern.charAt(oldValue.length)== delimiter){
oldValue = oldValue+delimiter;
inputField.setValue(oldValue);
}
}
}
}
else{
//key is not valid, so undo entry
inputField.setValue(oldValue);
evt.cancel();
}
}
}

The sample is for number only input. However, changing it for character or mixed input is not difficult to do. Note however that you can't use this with af:inputDate component because this component doesn't work well when setting String formatted values as the value property. (At least my debugging showed this)

About

The Oracle JDeveloper forum ranks in the Top 5 of the most active forums on the Oracle Technology Network (OTN).



The OTN Harvest blog is a summary of selected topics posted on the OTN Oracle JDeveloper forum.



It is an effort to turn knowledge exchange into an interesting read for developers who enjoy little nuggets of wisdom





Frank Nimphius

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today