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




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.


Sunday Oct 07, 2012

How-to get the binding for a tab in the Dynamic Tab Shell Template

The Dynamic Tab Shell template does expose a method on the Tab.java class that allows you to get access to the ADF binding container for a tab. At least in theory this works, because in practice this call always returns a null value (a bug is filed for this).

To work around the problem, you can use code similar to the following to get the ADF binding for a specific tab

DCBindingContainer currentBinding = (DCBindingContainer) BindingContext.getCurrent().getCurrentBindingsEntry();
DCBindingContainer templateBinding = (DCBindingContainer)currentBinding.get("ptb1");
DCBindingContainer tabBinding= (DCBindingContainer)templateBinding.get("r"+0); 

 In the code line above, the tabBinding variable will hold the binding reference to the first tab in the dynamic tab shell template. Note that the tab doesn't need to be visible for this (which has to do with how the template works).

 "ptb1" is the template reference name in the PageDef file (Executable section) of the template consumer view. Check this string in your page before using this code. If it differs, change it also in the code above.

"r0" is the binding reference of the first tab in the template. Te last tab is referenced by "r14".

 

Thursday Sep 20, 2012

Displaying Exceptions Thrown or Caught in Managed Beans

Just came a cross a sample written by Steve Muench, which somewhere deep in its implementation details uses the following code to route exceptions to the ADF binding layer to be handled by the ADF model error handler (which can be customized by overriding the DCErrorHandlerImpl class and configuring the custom class in DataBindings.cpx file)

To route an exception to the ADFm error handler, Steve used the following code

((DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry()).reportException(ex); 

The same code however can be used in managed beans as well to enforce consistent error handling in ADF. As an example, lets assume a managed bean method hits an exception. To simulate this, let's use the following code:

public void onToolBarButtonAction(ActionEvent actionEvent) {
   throw new JboException("Just to tease you !!!!!");        
}

The exception shows at runtime as displayed in the following image:

Exception in Managed Bean

Assuming a try-catch block is used to intercept the exception caused by a managed bean action, you can route the error message display to the ADF model error handler. Again, let's simulate the code that would need to go into a try-catch block

public void onToolBarButtonAction(ActionEvent actionEvent) {
   JboException ex = new JboException("Just to tease you !!!!!"); 
   BindingContext bctx = BindingContext.getCurrent();
   ((DCBindingContainer)bctx.getCurrentBindingsEntry()).reportException(ex);
}

The error now displays as shown in the image below

Exception displayed by ADFm error handler

As you can see, the error is now handled by the ADFm Error handler, which - as mentioned before - could be a custom error handler. Using the ADF model error handling for displaying exceptions thrown in managed beans require the current ADF Faces page to have an associated PageDef file (which is the case if the page or view contains ADF bound components).

Note that to invoke methods exposed on the business service it is recommended to always work through the binding layer (method binding) so that in case of an error the ADF model error handler is automatically used.



Wednesday Sep 12, 2012

Iterating selected rows in an ADF Faces table

In OTN Harvest May 2012; http://www.oracle.com/technetwork/developer-tools/adf/learnmore/may2012-otn-harvest-1652358.pdf I wrote about "Common mistake when iterating <af:table> rows". In this entry I showed code to access the row associated with a selected table row from the binding layer to avoid the problem of having to programmatically change the selected table row.

As it turns out, my solution only worked fro selected table rows that are in the current iterator query range. So here's a solution that works for all ranges

public String onButtonPress() {
  RowKeySet rks = table.getSelectedRowKeys();
  Iterator it = rks.iterator();
  while (it.hasNext()) {
   List selectedRowKeyPath = (List)it.next();            
   //table is the JSF component reference created using the table's binding
   //property         
   Row row = ((JUCtrlHierNodeBinding)table.getRowData(selectedRowKeyPath)).getRow();
   System.out.println("Print Test: " + row.getAttribute(1));
  }
   return null;
}


Tuesday Jul 31, 2012

Warning: java.lang.IllegalArgumentException: The file .jsff is not a source file

Randomly I experienced the following error when working with JSFF page fragments in JDeveloper 11g R2 (11.1.2.2):

Warning: java.lang.IllegalArgumentException: The file file:<directory listed here>\DepartmentsView.jsff is not a source file

This happened when I compiled the project. As it seems the compiler treats JSFF page fragments as Java source files. This issue has been reported as bug 12732652 for applications using Facelets as the page source holder. The fix for both cases, JSF pages as JSPX and Facelets, is to edit the web.xml file as follows

  <!-- 
  * WORK AROUND
  * randomly, when editing files, compilation fails with an illegal argument exception
  * java.lang.IllegalArgumentException: The file <file name> .jsff is not a source file
  * To avoid this error, which initially has been reported as bug 12732652 for Facelets
  * I added the jsp-property-group entry below
 -->
 <jsp-config>
  <jsp-property-group> 
    <url-pattern>*.jsff</url-pattern>
    <is-xml>true</is-xml>
   </jsp-property-group>
  </jsp-config>
 <!-- 
  * END WORK AROUND
  -->

Just add the above snippet before the " <mime-mapping>" entry in web.xml. At least for me, this did the trick

Monday Jul 23, 2012

How-to reset ADF Faces inputFile components

When working with the ADF Faces input file component, you'll notice a change in the UI after uploading a file in that the file name is displayed as a label and the upload button text changes to Update.

From a use case perspective this behavior may be fine if you bind the component value property to a managed bean that you want to update with changed file uploads. For generic file upload functionality though you probably prefer the input file to reset its state to the original display: input field and upload button text as "Browse".

To achieve this, you need to explicitly reset the input file component in a call to resetValue() on the RichInputFile component instance.

You can access the RichInputFile component instance from a JSF component binding to a manage bean (sub-optimal) or from a dynamic component lookup (recommended) in the component tree, using the UIViewRoot as a starting point or using Apache MyFaces Trinidad ComponentUtils:

http://myfaces.apache.org/trinidad/trinidad-api/apidocs/org/apache/myfaces/trinidad/util/ ComponentUtils.html

Thursday Jun 21, 2012

Partial Submit vs. Auto Submit

Partial Submit

ADF Faces adds the concept of partial form submit to JavaServer Faces 1.2 and beyond. A partial submit actually is a form submit that does not require a page refresh and only updates components in the view that are referenced from the command component PartialTriggers property. Another option for refreshing a component in response to a partial submit is call AdfContext.getCurrentInstance.addPartialTarget(component_instance_handle_goes_here)in a managed bean. If a form contains required fields that the user left empty invoking the partial submit, then errors are shown for each of the field as the full form gets submitted.

Autosubmit

An input component that has its autosubmit property set to true also performs a partial submit of the form. However, this time it doesn't submit the entire form but only the component that triggers the submit plus components referenced it in their PartialTriggers property.

For example, consider a form that has three input fields inpA, inpB and inpC with autosubmit=true set on inpA and required=true set on inpB and inpC.

use case 1: Running the view, entering data into inpA and then tabbing out of the field will submit the content for inpA but not for inpB and inpC. Further more, none of the required field settings on inpB and inpC causes an error.

use case 2: You change the configuration of inpC and set its PartialTriggers property to point to the ID of component inpA. When rerunning the sample, entering a value into inpA and tabbing out of the field will now submit the inpA and inpC fields and thus show an error for the missing required value on inpC.

Internally, using autosubmit=true on an input component sets the event root to just this field, which good to have in case of dependent field validation or behavior. The event root can extended to include other components by using the Partial Triggers property on these components to point to the input field that has autosubmit=true defined.

PartialSubmit vs. AutoSubmit

Partial submit set on a command component submits the whole form and leaves it to the developer to decide which UI component is refreshed in response. Client side required field validation (as well as the server side equivalent) is not disabled by executed in this scenario. Setting immediate=true on the command item to skip validation doesn't help as it would also skip the model update.

Auto submit is a functionality on the input components and also performs a partial form submit. However, in addition an event root is defined that narrows the scope for the submitted data and thus the components that are validated on the request.

To read more about this topic, see: http://docs.oracle.com/cd/E23943_01/web.1111/b31973/af_lifecycle.htm#CIAHCFJF

Monday Jun 18, 2012

Pretty URL in ADF Faces of JDeveloper 11.1.2.2

Many features planned for Oracle JDeveloper 12c find their way into current releases of Oracle JDeveloper 11g R1 and JDeveloper 11g R2. One example of such a feature is "pretty URL" - or "clean URL" as the Oracle JDeveloper 11g R2 (11.1.2.2) documentation puts it.

"A.2.3.24 Clean URLs

Historically, ADF Faces has used URL parameters to hold information, such as window IDs and state. However, URL parameters can prevent search engines from recognizing when URLs are actually the same, and therefore interfere with analytics. URL parameters can also interfere with bookmarking.

By default, ADF Faces removes URL parameters using the HTML5 History Management API. If that API is unavailable, then session cookies are used.

You can also manually configure how URL parameters are removed using the context parameter oracle.adf.view.rich.prettyURL.OPTIONS. Set the parameter to off so that no parameters are removed. Set the parameter to useHistoryApi to only use the HTML5 History Management API. If a browser does not support this API, then no parameters will be removed. Set the parameter to useCookies to use session cookies to remove parameters. If the browser does not support cookies, then no parameters will be removed."

See: http://docs.oracle.com/cd/E26098_01/web.1112/e16181/ap_config.htm#ADFUI12856

So basically, what this part in the documentation says is:

  • In JDeveloper 11g R2 (11.1.2.2), Oracle ADF Faces automatically removes its internally used dynamic parameters from the URL
  • You can influence the setting with the prettyURL.OPTIONS context option, which however is not recommended you to do because the default behavior is able to detect if the browser client supports HTML 5 History management or not. In the latter case it the uses a session cookie and if this doesn't work, falls back to the "old" URL parameter adding.
The information that is not so explicit and clearly mentioned in the documentation is that this is only for ADF Faces parameters (such as _afrLoop, Adf-Window-Id, etc.), but not the ADF controller token (_adf.ctrl-state)! Removing the ADF controller token is an enhancement request that will be implemented in Oracle JDeveloper 12c

af:inputSlider doesn't render in popup for FF, Safari and Chrome

A problem reported on OTN is that the af:inputSlider component of Oracle JDeveloper 11.1.2.2 doesn't show on all browsers except IE when the slider is added as the sole component in a popup. The problem reproduces with the ADF Faces component demo and I filed bug 14207690. The work around, posted by OTN user "Tses" is to set the inlineStyle property on the slider to table

<af:inputNumberSlider ... inlineStyle="display:table;"/>

Tuesday Jun 12, 2012

Programmatically disclosing a node in af:tree and af:treeTable

A common developer requirement when working with af:tree or af:treeTable components is to programmatically disclose (expand) a specific node in the tree.

If the node to disclose is not a top level node, like a location in a LocationsView -> DepartmentsView -> EmployeesView hierarchy, you need to also disclose the node's parent node hierarchy for application users to see the fully expanded tree node structure. Working on ADF Code Corner sample #101, I wrote the following code lines that show a generic option for disclosing a tree node starting from a handle to the node to disclose.

The use case in ADF Coder Corner sample #101 is a drag and drop operation from a table component to a tree to relocate employees to a new department. The tree node that receives the drop is a department node contained in a location. In theory the location could be part of a country and so on to indicate the depth the tree may have. Based on this structure, the code below provides a generic solution to parse the current node parent nodes and its child nodes.

The drop event provided a rowKey for the tree node that received the drop. Like in af:table, the tree row key is not of type oracle.jbo.domain.Key but an implementation of java.util.List that contains the row keys. The JUCtrlHierBinding class in the ADF Binding layer that represents the ADF tree binding at runtime provides a method named findNodeByKeyPath that allows you to get a handle to the JUCtrlHierNodeBinding instance that represents a tree node in the binding layer.

CollectionModel model = (CollectionModel) your_af_tree_reference.getValue();
JUCtrlHierBinding treeBinding = (JUCtrlHierBinding ) model.getWrappedData();
JUCtrlHierNodeBinding treeDropNode = treeBinding.findNodeByKeyPath(dropRowKey);

To disclose the tree node, you need to create a RowKeySet, which you do using the RowKeySetImpl class. Because the RowKeySet replaces any existing row key set in the tree, all other nodes are automatically closed.

RowKeySetImpl rksImpl = new RowKeySetImpl();
//the first key to add is the node that received the drop
//operation (departments).            
rksImpl.add(dropRowKey);    

Similar, from the tree binding, the root node can be obtained. The root node is the end of all parent node iteration and therefore important.

JUCtrlHierNodeBinding rootNode = treeBinding.getRootNodeBinding();

The following code obtains a reference to the hierarchy of parent nodes until the root node is found.

JUCtrlHierNodeBinding dropNodeParent = treeDropNode.getParent();
//walk up the tree to expand all parent nodes 
while(dropNodeParent != null && dropNodeParent != rootNode){
   //add the node's keyPath (remember its a List) to the row key set
   rksImpl.add(dropNodeParent.getKeyPath());   
   dropNodeParent = dropNodeParent.getParent();
}

Next, you disclose the drop node immediate child nodes as otherwise all you see is the department node. Its not quite exactly "dinner for one", but the procedure is very similar to the one handling the parent node keys

ArrayList<JUCtrlHierNodeBinding> childList = (ArrayList<JUCtrlHierNodeBinding>) treeDropNode.getChildren();                     
for(JUCtrlHierNodeBinding nb : childList){
  rksImpl.add(nb.getKeyPath());
}

Next, the row key set is defined as the disclosed row keys on the tree so when you refresh (PPR) the tree, the new disclosed state shows

tree.setDisclosedRowKeys(rksImpl); 
AdfFacesContext.getCurrentInstance().addPartialTarget(tree.getParent()); 

The refresh in my use case is on the tree parent component (a layout container), which usually shows the best effect for refreshing the tree component. 

Monday Jun 11, 2012

Solving the context menu problem with drag and drop in trees

The following drag-and-drop problem has been reported on OTN: An ADF Faces tree component is configured with a af:collectionDropTarget tag to handle drop events. The same tree component also has a context menu defined that is shown when users select the tree with the right mouse button. The problem now was - and I could reproduce this - that the context menu stopped working after the first time the tree handled a drop event. The drag and drop use case is to associate employees from a table to a department in the tree using drag and drop.

The drop handler code in the managed bean looked up the tree node that received the drop event to determine the department ID to assign to the employee. For this code similar to the one shown below was used

List dropRowKey = (List) dropEvent.getDropSite();
//if no dropsite then drop area was not a data area
if(dropRowKey == null){
   return DnDAction.NONE;
}                

tree.setRowKey(dropRowKey);
JUCtrlHierNodeBinding dropNode = (JUCtrlHierNodeBinding) tree.getRowData();

So what happens in this code? The drop event contains the dropSite reference, which is the row key of the tree node that received the drop event. The code then sets the key to the tree in a call to getRowDate() returns the node information for the drop target (the department). This however causes the tree state to go out of synch with its model (ADF tree binding), which is known to cause issues.

In this use case the issue caused by this is that the context menu no longer shows up. To fix the problem, the code needs to be changes to read the current row key from the key, then perform the drop operation and at the end set the origin (or model) row key back

//memorize current row key
Object currentRowKey = tree.getRowKey();        
List dropRowKey = (List) dropEvent.getDropSite();
//if no dropsite then drop area was not a data area
  if(dropRowKey == null){
    return DnDAction.NONE;
  }              
tree.setRowKey(dropRowKey);
JUCtrlHierNodeBinding dropNode = (JUCtrlHierNodeBinding) tree.getRowData();
... do your stuff here ....
//set current row key back
tree.setRowKey(currentRowKey);
AdfFacesContext.getCurrentInstance().addPartialTarget(tree);

Note the code line that sets the row key back to its original value.

Wednesday May 30, 2012

Problem with deleting table rows using ctrl+a for row selection

The following code is commonly shown and documented for how to access the row key of selected table rows in an ADF Faces table configured for multi row selection.

public void onRemoveSelectedTableRows(ActionEvent actionEvent) {
   RichTable richTable = … get access to your table instance …
   CollectionModel cm =(CollectionModel)richTable.getValue();
   RowKeySet rowKeySet = (RowKeySet)richTable.getSelectedRowKeys();        
  
   for (Object key : rowKeySet) {
      richTable.setRowKey(key);
      JUCtrlHierNodeBinding rowData = (JUCtrlHierNodeBinding)cm.getRowData();
      // do something with rowData e.g.update, print, copy
   }
   //optional, if you changed data, refresh the table      
   AdfFacesContext adfFacesContext = AdfFacesContext.getCurrentInstance();
   adfFacesContext.addPartialTarget(richTable);  
   return null;
}

The code shown above works for 99.5 % of all use cases that deal with multi row selection enabled ADF Faces tables, except for when users use the ctrl+a key to mark all rows for delete. Just to make sure I am clear: if you use ctrl+a to mark rows to perform any other operation on them – like bulk updating all rows for a specific attribute – then this works with the code shown above. Even for bulk row delete, any other mean of row selection (shift+click and multiple ctrl+click) works like a charm and the rows are deleted.

So apparently it is the use of ctrl+a that causes the problem when deleting multiple rows of an ADF Faces table. To implement code that works for all table selection use cases, including the one to delete all table rows in one go, you use the code shown below.

public void onRemoveSelectedTableRows(ActionEvent actionEvent) {
  RichTable richTable = … get access to your table instance …
  CollectionModel cm = (CollectionModel)richTable.getValue();
  RowKeySet rowKeySet = (RowKeySet)richTable.getSelectedRowKeys();

  Object[] rowKeySetArray = rowKeySet.toArray();   

  for (Object key : rowKeySetArray){           
    richTable.setRowKey(key);
    JUCtrlHierNodeBinding rowData = (JUCtrlHierNodeBinding)cm.getRowData();                           
    rowData.getRow().remove();
  }
  
  AdfFacesContext adfFacesContext = AdfFacesContext.getCurrentInstance();          
  adfFacesContext.addPartialTarget(richTable);
}

Solving File Upload Cancel Issue

In Oracle JDeveloper 11g R1 (I did not test 11g R2) the file upload component is submitted even if users click a cancel button with immediate="true" set. Usually, immediate="true" on a command button by-passes all modle updates, which would make you think that the file upload isn't processed either. However, using a form like shown below, pressing the cancel button has no effect in that the file upload is not suppressed.

Update Aug 29/2012: Appears that all the af:subform suggestions in the original blog entry did not work out for any who tried it. The form still appears to be submitted as a whole. To effectively cancel an upload form, use the af:goButton or af:goLink as the cancel button or lnk, which does not submit the form at all.

Tuesday May 08, 2012

Managing component value state correctly

A frequent problem reported on OTN is about failure in saving component values using managed beans especially when multi-select components are used. Usually the problem arises when developers store select choice components values in managed beans they use for JSF component bindings as well (which then makes the managed bean becoming a backing bean). Backing beans however are saved in request scope (at least they should to not err again).

Request scope beans however reset their state after each request, which means that no user update in a select component is persisted beyond the request. The result is that user changes are not displayed in the UI or value change listeners don't execute.The proper way of handling the combination of backing beans and component values saved in a managed bean is to come up with a strategy in which the component value is written to a separate bean in a broader scope (I usually recommend viewScope) than the backing bean. The backing bean (request scope) would then access the bean in the larger scope using a ValueExpression or managed bean property to access the user selected data.

I was about to write this solution up in more detail but then found the following blog entry by Duncan Mills:

https://blogs.oracle.com/groundside/entry/the_uimanager_pattern

In his The UIManager Pattern blog entry, Duncan documented the same problem from a different angle. The programmer mistake Duncan observed was that component binding references (the Bindings property reference on an ADF Faces component) were stored in managed beans with a scope larger than request, which JavaServer Faces component don't support.

 In his write-up Duncan documents the same strategy I was about to document, which is to separate the value saving from the component reference using different managed beans in different scope. Well, I think that Duncan's blog entry saved me some time I today.

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