Showing posts with label JS. Show all posts
Showing posts with label JS. Show all posts

Wednesday, 19 February 2014

Usage of JavaScript in ADF Faces Rich Client Applications: guidelines and best practices

Using JS into ADF application is often considered a controversial topic.

I this white paper of ADF Design Fundamentals series, Franck Nimphius provides a guideline about how to properly use JS into an ADF application:

http://www.oracle.com/technetwork/developer-tools/jdev/1-2011-javascript-302460.pdf

The bottom line is:

If JavaScript is used, developers are encouraged to only use public ADF Faces client framework APIs instead of direct browser DOM manipulation and to stay away from using ADF Faces JavaScript objects stored in internal package hierarchies. 

This list of 10 best practices is extracted from the white paper above:

  • Best Practice 1: An oven alone doesn’t make a cook. Developers should ensure they understand the ADF Faces client architecture before using JavaScript in ADF Faces applications. 
  • Best Practice 2: Protected, package, or private methods and variables that DOM inspection tools like Firebug show for ADF Faces components and events should not be used in custom ADF Faces application development. 
  • Best Practice 3: Application developers should ensure that a component exists on the client before accessing it from JavaScript. ADF Faces client component objects are created by setting the component clientComponent property to true or adding an af:clientListener tag. 
  • Best Practice 4: Developers should not hardcode any client component id that shows in the generated HTML output for a page into their JavaScript functions. Client id values may change even between runs of a view. Instead developers can dynamically determine the client Id by a call to the ADF Faces component's getClientId method in a managed bean. 
  • Best Practice 5: Application developers should call cancel()on all events that don't need to propagate to the server. 
  • Best Practice 6: Client-to-server calls should be used sensibly to reduce the amount of network roundtrips and thus to prevent slow performance due to network latency. 
  • Best Practice 7: No knowledge about framework internal implementation details should be used directly in JavaScript code. Instead, public APIs and constants should always be used. 
  • Best Practice 8: JavaScript exceptions should be handled gracefully and not just suppressed. 
  • Best Practice 9: The framework default behavior should be respected. The rich client component architecture and its APIs are performance optimized. Component functionality therefore should not be changed by call to the client component prototype handler. 
  • Best Practice 10: JavaScript should not be used for implementing application security.

Friday, 14 February 2014

Showing popup in ADF during long running operations


An ADF application can trigger long running operations (DB queries, WS calls, etc), and this could cause user experience issues: user getting impatient, clicking everywhere and (possibly) crashing the application causing unpredictable behaviour (double submitting forms, modifying input data while running).

The following one is a solution proposed from Frank Nimphius in this article of ADF Code Corner: for every long running operation, we show an ADF popup (it's our choice if we want to make it modal or not), making a JS function call via the clientListener component.

The solution is based on 3 steps:

1) Create the popup component, making sure that contentDelivery is set to immediate (to render it when the page loads) and clientComponent is set to true (to make it accessible from JS):

<af:popup id="waitPopup" contentDelivery="immediate" clientComponent="true">
  <af:dialog id="waitDialog" type="none"
             title="#{resBundle.WAIT_POPUP_TITLE}" closeIconVisible="false">
    <af:panelGroupLayout id="pgl2" layout="vertical" halign="center">
     <af:image source="/images/scan.gif" id="i1"/>
     <af:outputText value="#{resBundle.WAIT_POPUP_MSG}" id="waitText"/>
    </af:panelGroupLayout>
  </af:dialog>
</af:popup>

2) Create JS code to disable the user input and open the popup; create a file named waitingpopup.js in a subfolder of public_html folder in your application, and write the following code inside it:
function enforcePreventUserInput(evt) {
    var popup = AdfPage.PAGE.findComponentByAbsoluteId('waitPopup');
    if (popup != null) {
        AdfPage.PAGE.addBusyStateListener(popup, handleBusyState);
        evt.preventUserInput();
    }
}

function handleBusyState(evt) {
    var popup = AdfPage.PAGE.findComponentByAbsoluteId('waitPopup');
    if (popup != null) {
        if (evt.isBusy()) {
            popup.show();
        }
        else if(popup.isPopupVisible()) {
            popup.hide();
            AdfPage.PAGE.removeBusyStateListener(popup, handleBusyState);
        }
    }
} 

After the file is saved, put the resource tag in the page/fragment containing the popup, using the correct path of the file created above (in this case the file has been created under public_html/scripts folder):
<af:resource type="javascript" source="/scripts/waitingpopup.js"/>

3) Trigger the popup by invoking the JS code above via clientListener:

<af:commandLink id="commandLink4" styleClass="btn btn-alt"
    disabled="#{isTaskRunning}"
    actionListener="#{bindings.longOperation.execute}"
    action="back"> 
  <af:clientListener method="enforcePreventUserInput" type="action"/> 
</af:commandLink>


What is not optimal in this solution is the part in which the JS retrieves the popup component by ID: this approach works smoothly if the popup is defined at top level (inside a JSF page or a JSF template: if the popup is defined inside a fragment, the ID for this component at runtime will be prefixed by the id of the region (something like 'r0:waitingPopup'), and in case the ADF taskflow containing this fragment is shown (for example) in a Portal page that is using a template, the component id is prefixed with the ID of the template as well; bottom line is, define this popup always at top level, or the code above won't always work.

Saturday, 5 November 2011

How to call JavaScript from a ADF Managed Bean

To run a JS function from our Managed Bean, first put the JS code in a <af:resource> component in our JSF page:
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1" 
           xmlns:f="http://java.sun.com/jsf/core" 
           xmlns:pe="http://xmlns.oracle.com/adf/pageeditor" 
           xmlns:cust="http://xmlns.oracle.com/adf/faces/customizable" 
           xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
<af:resource type="javascript">
    function myFunc() { [ any JS code ] };
</af:resource>

In the managed bean, put the following code in the appropriate method (e.g. in a action listener if we want to execute the JS code after a commandButton or commandLink is selected):
import javax.faces.context.FacesContext;
import org.apache.myfaces.trinidad.render.ExtendedRenderKitService;
import org.apache.myfaces.trinidad.util.Service;
[…]
FacesContext facesContext = FacesContext.getCurrentInstance(); 
ExtendedRenderKitService service = (ExtendedRenderKitService) org.apache.myfaces.trinidad.util.Service.getRenderKitService(facesContext, ExtendedRenderKitService.class);
service.addScript(facesContext, "myFunc();");