Showing posts with label 11g. Show all posts
Showing posts with label 11g. Show all posts

Monday, June 30, 2014

ADF: jQuery Auto Suggest Implementation


Oracle 11gIn this post we will see how to integrate the one of the jquery's famous plugins, the ui autosuggest component with an Oracle Webcenter application using ADF faces. This implementation assumes that the requirements around the autosuggest doesn't meet the requirements of the af:autoSuggestBehavior. So lets first discuss why is the out of box auto suggest behaviour in behavior in adf not that desirable.



Cases where af:autoSuggestBehavior is useful

  1. If you have a large complex dataset ( in tunes of +5000 objects)
  2. If the performance / experience of the autosuggest is not a concern

Cases where custom implementation is useful

  1. The autoSuggestBehavior is not a "client only" side implementation so every change in the value triggers an expensive backend call even if there is a static list of values.
  2. Not every time we require to fetch data from backing bean, there are times and I believe for most of the cases we already know the data for which we are applying autoSuggest.
  3. The lack of flexibility to add custom behavior to suggested output is difficult.


So lets build our awesome autosuggest. Assume we have a requirement of auto-suggesting the user from a list of countries. Here's the steps

Step 1 : Add required libraries to our template


Libraries required:
  1. Add jquery core library, if not present already
  2. Add jquery ui custom library ( including the autosuggest plugin )
  3. Also we need GSON jar in classpath ( optional ). Its useful to flush out JSON in proper format

1
2
<af:resource type="javascript" source="/js/jquery-1.11.0.min.js"/>
<af:resource type="javascript" source="/js/jquery-ui-1.10.1.custom.min.js"/>

Step 2: Add a clientListener

The clientListener will fire a client event on focus to a javascript method suggestContries

1
2
3
4
5
6
<af:inputText label="#{bundle.COUNTRIES}" id="it1"
  clientComponent="true" styleClass="countriesBox"
  value="#{myBean.country}"
  immediate="false">
 <af:clientListener method="suggestCountries" type="focus"/>
</af:inputText>


Step 3: Add a method in backing bean


Lets create a method in our backing bean to initialize the data for autosuggest. The method flushes out javascript variable data to the client.The flushed out data contains the list of countries in JSON format. On executing the method we get the JSON string in a JS variable called countriesJS


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
31
32
33
34
35
public void getCountriesJSON() {
 
 StringBuilder sb1 = new StringBuilder();
 try {
  /*
  * Get all countries:
  */
  //TODO: replace retrieveCountries method to get the set of CountryData objects
  Set<CountryData> countries = retrieveCountries();
  // create an instance of JsonArray
  JsonArray countriesJsonArray = new JsonArray();           
  sb1.append(" countriesJS=");
  Iterator<CountryData> iter = countries.iterator();

  while (iter.hasNext()) {
   CountryData countryData = iter.next();
   JsonObject countryDataObjJson = new JsonObject();

   countryDataObjJson.addProperty("label", countryData.getCountryName());
   countryDataObjJson.addProperty("value", countryData.getCountryCode().trim());

   countriesJsonArray.add(countryDataObjJson);
   }


  sb1.append(countriesJsonArray.toString());

 } catch (Exception e) {
  e.printStackTrace();
 }
 /*
  * Utility method to add the product json object to client page
  */
 addScriptOnRequest(sb1.toString());
}


Utlity method to add script on request:


1
2
3
4
5
6
public static void addScriptOnRequest(String script) {
 FacesContext context = FacesContext.getCurrentInstance();
 ExtendedRenderKitService erks = 
 Service.getRenderKitService(context, ExtendedRenderKitService.class);
 erks.addScript(context, script);
}

Step 4: Get the data on page load


Now lets add the following code to the page where you have the autosuggest input box. This piece of code will call getCountriesJSON method in your backing bean and load the JSON data on the client.


1
2
3
4
<af:document id="d1" title="#{bundle.MY_COUNTRIES}">
 <af:clientListener type="load" method="loadCountriesOnLoad"/>
 <af:serverListener type="loadCountriesEvt" method="#{myBean.getCountriesJSON}"/>
</af:document>


Add loadCountriesOnLoad Javascript method


1
2
3
4
5
6
 function loadCountriesOnLoad(evt)
{
 var customEvent = new AdfCustomEvent(evt.getSource(),"loadCountriesEvt",{}, true); 
 customEvent.preventUserInput();
 customEvent.queue(true);
}


So when page loads we will have the countriesJS is assigned to the following JSON


1
2
3
4
[
    {"label":"Afghanistan", "value":"AFG"}, 
    {"label":"Albania", "value":"ALB"}
]


Step 5: Wiring everything up


We will write the final JavaScript method mentioned on the onFocus event mentioned in Step 1
 - suggestCountries


1
2
3
4
5
6
7
8
9
10
11
12
13
14
function suggestCountries(evt) {
    var src = document.getElementById(evt.getSource().getClientId() + '::content');
    countriesComp = $(src).autocomplete( {
        "source" : countriesJS, "minLength" : 2,autoFocus: false,
        select : function (event, ui) {
           
        }
    }).autocomplete( "instance" )._renderItem = function( ul, item ) {
      return $( "<li>" )
        .append( "<a>" + item.label + "<br>" + item.value + "</a>" )
        .appendTo( ul );
    };
    
}


so here you go, we have the all client side auto suggest implemented !!! Please let me know what you think about the approach.


Friday, June 27, 2014

ADF Code Examples: af:selectOneChoice


Oracle 11gThis component generates a menu link with a single menu selection model. The value that goes to backing bean by default is an index (0,1,2) unless the valuePassThru is true. If valuePassThru is false then the selection value is based on indices. I am trying to use as much properties in the examples but please free to use them selectively.



Populating af:selectOneChoice  using af:forEach

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<af:selectOneChoice id="sc1" clientComponent="true"
  required="false" immediate="true" 
  shortDesc="#{bundle.TYPES}"
  showRequired="true" mode="default" 
  contentStyle="width:30px" 
  binding="#{pageFlowScope.myBean.types}"
  value="#{pageFlowScope.myBean.type}"
  unselectedLabel="#{bundle.SELECT_ONE}"
  label="#{sprportalBundle.TYPES}:"
  autoSubmit="false">
 <af:forEach items="#{bindings.typeLOV.iteratorBinding.allRowsInRange}"
   var="item">
  <f:selectItem id="sit1" itemLabel="#{item.type}"
    itemValue="#{item.type}"/>
 </af:forEach>
</af:selectOneChoice>


Populating af:selectOneChoice  using individual af:selectItem tags

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<af:selectOneChoice label="#{bundle.MYLABEL}"
  id="xs1" required="false"
  immediate="true"
  valueChangeListener="#{pageFlowScope.myBean.typesVCL}"
  showRequired="true"
  value="#{pageFlowScope.myBean.type}"
  binding="#{pageFlowScope.myBean.typeBinding}"
  styleClass="classname">
 <af:selectItem label="#{bundle.CHOICE_TYPE1}"
   value="#{pageFlowScope.myBean.typeValue1}"
   id="xs2"/>
 <af:selectItem label="#{bundle.CHOICE_TYPE1}"
   value="#{pageFlowScope.myBean.typeValue2}"
   id="xs3"
   rendered="#{pageFlowScope.myBean.typeValueShown}"/>
</af:selectOneChoice>

Populating af:selectOneChoice  using af:selectItems tag


1
2
3
4
5
6
7
8
 <af:selectOneChoice value="#{bindings.Team.inputValue}"  label="#{bundle.MYLABEL}"                      
  shortDesc="#{bindings.type.hints.tooltip}" id="xc2"
  binding="#{pageFlowScope.myBean.team}" autoSubmit="true"
  unselectedLabel="#{bundle.MYLABEL}"
  rendered="#{not pageFlowScope.myBean.noTeams}"
  simple="true">
 <f:selectItems value="#{bindings.Team.items}" id="xc3"/>
</af:selectOneChoice>


ADF Code Examples: af:commandButton [deprecated]


Oracle 11gThis post provides different flavors of af:commandButton usage however the component itself has been deprecated in favor of af:button (super button) in 12c. A command button generates an action event  on the server following a button press which largely differs from the behavior of a goButton.



Using af:commandButton to call an action method with params in your bean


1
2
3
4
5
6
7
8
9
<af:commandButton text="#{bundle.MY_BUTTON}"
  id="cl1"
  actionListener="#{myBean.actionMe}"
  partialSubmit="true"
  partialTriggers="cl2"
  rendered="#{myBean.showLink}">
 <af:setActionListener from="#{myBean.oneVariable}"
   to="#{myBean.otherVariable}"/>
</af:commandButton>


Using af:commandButton to open a popup


1
2
3
4
5
<af:commandButton text="#{bundle.MY_BUTTON}" id="cl1"
  partialSubmit="true"
  clientComponent="true">
 <af:showPopupBehavior triggerType="click" popupId="p1"/>
</af:commandButton>

Calling a JavaScript function with af:commandButton


1
2
3
4
5
6
7
8
9
 <af:commandButton text="#{bundle.MY_BUTTON}"
  id="cl1" 
  clientComponent="true"
  partialSubmit="true">
 <af:clientAttribute name="name"
   value="#{myBean.name}"/>
 <af:clientListener method="showAlert"
   type="click"/>
</af:commandButton>


Wednesday, June 25, 2014

ADF Code Examples: af:commandLink [Deprecated]


Oracle 11gThis post provides different flavors of af:commandLink usage however the component itself has been deprecated in favor of af:link in 12c. A command link generates an action event  on the server following a link click which largely differs from the behavior of a goLink.

Using af:commandLink to call an action method with params in your bean


1
2
3
4
5
6
7
8
9
<af:commandLink text="#{bundle.MY_LINK}"
  id="cl1"
  actionListener="#{myBean.actionMe}"
  partialSubmit="true"
  partialTriggers="cl2"
  rendered="#{myBean.showLink}">
 <af:setActionListener from="#{myBean.oneVariable}"
   to="#{myBean.otherVariable}"/>
</af:commandLink>


Using af:commandLink to open a popup


1
2
3
4
5
<af:commandLink text="#{bundle.SUI_RECENT_SEARCHES}" id="cl1"
  partialSubmit="true"
  clientComponent="true">
 <af:showPopupBehavior triggerType="click" popupId="p1"/>
</af:commandLink>

Calling a JavaScript function with af:commandLink


1
2
3
4
5
6
7
8
9
 <af:commandLink text="#{row.header}"
  id="cl1" 
  clientComponent="true"
  partialSubmit="true">
 <af:clientAttribute name="name"
   value="#{myBean.name}"/>
 <af:clientListener method="showAlert"
   type="click"/>
</af:commandLink>



Sunday, June 22, 2014

Global exception handler (oracle.adf.view.rich.context.ExceptionHandler) in ADF

By look of it how hard it can to simply register an exception handler to capture the "unhandled ones", well it took a hell lot of my time -  nearly a week spent to figure out what was that I was doing wrong. 


To give a context - I was trying to implement the global exception handler (adf_controller_exception_handler) by extending oracle.adf.view.rich.context.ExceptionHandler class and putting the fully qualified name of the implemented class in .adf/META-INF/services/oracle.adf.view.rich.context.ExceptionHandler.txt file.

I saw it working in local jDeveloper (Studio Edition Version 11.1.1.6.0) when deployed to integrated server it didn't work when deployed to dev. server. 


I banged my head a lot on this and was finally able solve it ! The issue was with the .adf/META-INF/services folder declaration - it seemed when deploying to server the exception handler was not getting registered. To debug the registered services you need to use the following.


  1.             
  2.           ServiceLoader compilers = ServiceLoader.load(ExceptionHandler.class);  
  3.             _logger.info(compilers.toString());  
  4.             for (ExceptionHandler compiler : compilers) {  
  5.                 _logger.info("******ExceptionHandler*********" + compiler);  
  6.             }  




If you check the loggers, the exception handler should be listed, if not then create a jar file with the exception handler class and the put the services folder along-with with the exception handler file (not a text file ) inside the META-INF of the jar file. Last step - put the jar file in WEB-INF/lib in your controller project.

ADF: navigation menu with go:Link


Oracle 11gThis post illustrates how a default navigation menu can be converted to use go link instead of command links. This will let users to use navigation tabs as simple links. Users can then be able to use browser basic functions as right click copy, share, open the link in new tab. The navigation itself will have simple go links which are also SEO friendly.



Implementation

Lets see how the current navigation model looks like:


1
2
3
4
5
<af:navigationPane hint="tabs" var="foo" value="#{xmlMenuModel} level="1">
  <f:facet name="nodeStamp">
    <af:commandNavigationItem text="#{foo.label}" action="#{foo.doAction}"/>
  </f:facet> 
</af:navigationPane>

Now we cannot simply have a goLink instead of a af:commandNavigationItem that is because af:navigationPane  supports only af:commandNavigationItem and if we keep using the commandNavigationItem then all the basic functions will not work. Also using a command navigation item makes an extra call before redirecting the user to requested page.

So do we have a workaround ? Yes ! and the end product will be like the following-



Things to consider, we need to get the dynamic URLs from default-navigation model and also we need to handle parameters that might be defined with the URL.

The following fragment code can be used instead of the standard pagemenudefinition task flow, of coarse you might want to create a TF out of it.


              
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?xml version='1.0' encoding='utf-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:f="http://java.sun.com/jsf/core" xmlns:af="http://xmlns.oracle.com/adf/faces/rich" version="2.1">
 <af:panelGroupLayout id="s290" styleClass="WCMenuNav">
  <div style="height: 33px;margin-left:-9px">
   <af:panelGroupLayout id="pgm16" styleClass="af_navigationPane af_navigationPane-tabs">
    <af:panelGroupLayout id="pgm14" inlineStyle="height:33px" styleClass="af_navigationPane-tabs_header">
     <af:panelGroupLayout id="pgm153" styleClass="af_navigationPane-tabs_body">
      <af:panelGroupLayout id="pgm2e" styleClass="af_navigationPane-tabs_content">
       <af:iterator var="node" varStatus="vs" value="#{navigationContext.defaultNavigationModel.listModel['startNode=/, includeStartNode=false']}" id="itor1">
        <af:switcher id="afs" facetName="#{node.selected ? 'current':'others'}">
         <f:facet name="others">
          <af:panelGroupLayout id="pgm2" styleClass="af_navigationPane-tabs_tab">
           <af:panelGroupLayout id="pgm3" styleClass="af_navigationPane-tabs_tab-start" inlineStyle="height:33px"/>
           <af:panelGroupLayout id="pgm4" styleClass="af_navigationPane-tabs_tab-content" inlineStyle="height:33px">
            <af:goLink id="gLink" styleClass="af_navigationPane-tabs_tab-link tab_link" text="#{node.title}" destination="#{node.prettyUrl}?#{node.parameters}"/>
           </af:panelGroupLayout>
           <af:panelGroupLayout id="pgm5" styleClass="af_navigationPane-tabs_tab-end" inlineStyle="height:33px"/>
          </af:panelGroupLayout>
         </f:facet>
         <f:facet name="current">
          <af:panelGroupLayout id="pgo2" styleClass="af_navigationPane-tabs_tab p_AFDisabled p_AFSelected">
           <af:panelGroupLayout id="pgo3" styleClass="af_navigationPane-tabs_tab-start" inlineStyle="height:33px"/>
           <af:panelGroupLayout id="pgo4" styleClass="af_navigationPane-tabs_tab-content" inlineStyle="height:33px">
            <af:outputText id="gLinko" styleClass="af_navigationPane-tabs_tab-link" value="#{node.title}"/>
           </af:panelGroupLayout>
           <af:panelGroupLayout id="pgo5" styleClass="af_navigationPane-tabs_tab-end" inlineStyle="height:33px"/>
          </af:panelGroupLayout>
         </f:facet>
        </af:switcher>
       </af:iterator>
      </af:panelGroupLayout>
     </af:panelGroupLayout>
    </af:panelGroupLayout>
   </af:panelGroupLayout>
  </div>
 </af:panelGroupLayout>
 <f:verbatim>
<script type="text/javascript"> 
//script to add parameters to URL 
          $(".af_navigationPane-tabs_tab-link.tab_link.af_goLink").each(function () {  
            var newURL = this.href.split("%7B").join('').split("%7D").join('').split("%2C").join('').split("%20").join('&amp;amp;');  
            if (newURL.charAt(newURL.length - 1) == '?') {  
              newURL = newURL.substring(0, newURL.length - 1);  
            }  
            this.href = newURL;  
          });  
</script>
</jsp:root>

 Here we go, we not have a tabbed navigation which is much more like web 2.0 style.