Thursday Jan 23, 2014

Disbling buttons when new record is created in ADF form

In some cases, we might want to disable some of the buttons / items in the page (say navigation buttons), when a new record is created in the form. 

To achieve this, we would start from Sireesha's excellent blog : https://blogs.oracle.com/adf/entry/highlighting_new_rows_in_adf  . In this blog, insight is given on entityState of a row. 

Before starting, assuming that we have a Form (Say based on Employees), with navigation, create, commit and rollback buttons.

Our usecase is to disable the navigation buttons when the user clicks on CreateInsert button. For this, we could set an EL to the disabled property of the navigation buttons.

Code snippet of the First button.

<af:commandButton actionListener="#{bindings.First.execute}"
                                  text="First"
                                  disabled="#{bindings.Empno.currentRow.entities[0].entityState == 0 or !bindings.First.enabled}"
                                  partialSubmit="true" id="cb2"/>

Here Empno refers to the one of the attribute binding of the form (Primary Key attribute to be precise).

This approach disables the buttons when the form goes to Insert mode (refer the blog mentioned above to find out more about entityState EL).



Monday Nov 25, 2013

ADF Faces Layout Basics - New Whitepaper Released

As the title suggests, basics of layout components available in ADF Faces is covered in this whitepaper.

This paper can be accessed from this location : http://www.oracle.com/technetwork/developer-tools/adf/learnmore/adffaceslayoutbasics-2046652.pdf

In a nutshell, this paper talks about

  • different layout components available in ADF
  • their geometry management
  • example situations during which a particular component can be used
  • reverse engineering some of the popular websites and
  • solutions to few frequent layout scenarios

This paper will be very helpful for understanding different layout components offered by ADF Faces and also to use them effectively to build your website.

You can also browse through ADF Architecture Square Homepage to find this and other related whitepapers : http://bit.ly/adfarchsquare

Wednesday Oct 23, 2013

Building Tag Cloud Declarative ADF Component

When building a website, there could a requirement to add a tag cloud to let the users know the popular tags (or terms) used in the site. In this blog, we would build a simple declarative component to be used as tag cloud in the page.

To start with, we would first create the declarative component, which could display the tag cloud. We will do that by creating a new custom application from the new gallery.

Give a name for the app and the project and from the new gallery, let us create a new ADF Declarative Component

We need to specify the name for the declarative component, attributes in it etc. as follows


For displaying the tags as cloud, we need to pass the content to this component. So, we will create an attribute to hold the values for the tag. Let us name it as "value" and make it as java.lang.String  type.

Once after this, to hold the component, we need to create a tag library. This can be done by clicking on the Add Tag Library button.

Clicking on OK buttons in all the open dialogs would create a declarative component for us. Now, we need to display the tag cloud based on the value passed to the component. To do that, we assume that the value is a Tree Binding and has two attributes in it, say "Name" and "Weight". To make a tag cloud, we would put together the "Name" in a loop and set it's font size based on the "Weight". After putting our logic to work, here is how the source look

Attributes added to the declarative components can be retrieved by using #{attrs.<attribute_name>}. Now, we need to deploy this project as ADF Library Jar file, so that this can be distributed to the consuming applications.

We'll select ADF Library Jar as type and create the profile.

We would be getting the jar file after deployment.

To test the functionality, we could create a simple Fusion Web Application.

To add our custom component to the consuming application, we can create a file system connection pointing to the location where the jar file is and add it or, add through the project properties of the ViewController project.

Now, our custom component has been added to the consuming application. We could test that by creating a VO in the model project with a query like,

select 'Faces' as Name,25 as Weight from dual
union all
select 'ADF', 15 from dual  
union all
select 'ADFdi', 30 from dual
union all
select 'BC4J', 20 from dual
union all
select 'EJB', 40 from dual
union all
select 'WS', 35 from dual

Add this VO to the AppModule, so that it would be exposed to the data control. Then, we could create a jspx page, and add a tree binding to the VO created.

We can now see our Tag Cloud declarative component is available in the component palette.


 It can be inserted from the component palette to our page and set it's value property to CollectionModel of the tree binding created.

Now that we've created the Declarative component and added that to our page successfully, we can run the page to see how it looks.

As per the query, the Tags are displayed in different fonts, based on their weight.

Thursday Jan 17, 2013

How to create cascading (depending) auto suggest behavior using BC4J

In continuation to my previous blog on "How to create multi level cascading (dependent) list of values using BC4J", i am trying to leverage a similar usecase using af:autoSuggestBehavior in this article. Though the usecase looks same, the implementation is slightly different for this.

Let us assume a usecase where we have some text fields with auto suggest feature enabled, and their suggested items could be dependent on one other. For ex : Country, State and City.

This could be modeled by having an EO and VO created based on Person table and read-only look-up VOs created based on Country, State and City tables.

model.jpg

VOs.jpg

Implementing the dependency between LOVs is pretty straight forward. However, implementing the dependency between the auto suggest items is not. In order to achieve the dependency, first we would need couple of methods returning current row's CountryId and StateId. We could add them up in the AMImpl class.

public Number getCurrentCountryId(){
return (Number) this.getPersonView1().getCurrentRow().getAttribute("CountryId");
}

public Number getCurrentStateId(){
return (Number) this.getPersonView1().getCurrentRow().getAttribute("StateId");
}

As we need to filter out the States based on the Country and the City based on the Country & State, we would need to modify the query of these two VOs to include a bind variable in the where clause.

whereClause.jpg

For having the auto suggest, we need to have a view criteria defined for all the three look-up VOs (CountryView, StateView and CityView).

CountryVC.jpg

StateVC.jpg

CityVC.jpg

Generate VOImpl classes for Country, State and City VOs (with Include bind variable accessors option checked), and then expose setBindCountryName (in Country VO), setBindStateName (in State VO) and setBindCityName (in City VO) methods as client interfaces.

And the last part on the model is to pass the current row's CountryId and StateId to the Bind Variables defined in the State and City VOs. Also, we need to get the VCs created above to be executed by default (By editing the VO instance in the AM's data model), so that the auto suggest list would be filtered as and when the users type.

CountryViewAM.jpg

StateViewAM.jpg

CityViewAM.jpg

Here, we specify groovy expression for the CountryId and StateId as adf.object.applicationModule.<methodName>. For more information about using groovy expressions, check out this : http://www.oracle.com/technetwork/developer-tools/jdev/introduction-to-groovy-128837.pdf .

With this, we are done with setting up the model layer for the auto suggest dependency.

In the View layer, we would create an ADF Form based on the Person VO, with all the navigation buttons.

PersonJSPX.jpg

In order to construct the onSuggest items, we would create Tree Bindings for Country VO, State VO and City VO, along with method action bindings for the setBindCountryName, setBindStateName and setBindCityName methods.

pagedef.jpg

Now, we could add af:autoSuggestBehavior for CountryId, StateId and CityId fields. Then, add onSuggest methods in backing bean for populating the on suggest items for each fields.

onSuggest method for Country field :

public List onCountrySuggest(String searchCountryName) {
ArrayList<SelectItem> selectItems = new ArrayList<SelectItem>();

System.out.println(searchCountryName);
//get access to the binding context and binding container at runtime
BindingContext bctx = BindingContext.getCurrent();
BindingContainer bindings = bctx.getCurrentBindingsEntry();
//set the bind variable value that is used to filter the View Object
//query of the suggest list. The View Object instance has a View
//Criteria assigned
OperationBinding setVariable = (OperationBinding) bindings.get("setBind_CountryName");
setVariable.getParamsMap().put("value", searchCountryName);
setVariable.execute();
//the data in the suggest list is queried by a tree binding.
JUCtrlHierBinding hierBinding = (JUCtrlHierBinding) bindings.get("CountryView1");


//re-query the list based on the new bind variable values
hierBinding.executeQuery();

//The rangeSet, the list of queries entries, is of type
//JUCtrlValueBndingRef.
List<JUCtrlValueBindingRef> displayDataList = hierBinding.getRangeSet();

for (JUCtrlValueBindingRef displayData : displayDataList){
Row rw = displayData.getRow();
//populate the SelectItem list
selectItems.add(new SelectItem(
(Integer)rw.getAttribute("Id"),
(String)rw.getAttribute("Name")));
}

return selectItems;
}

onSuggest method for State field :


public List onStateSuggest(String searchStateName) {
ArrayList<SelectItem> selectItems = new ArrayList<SelectItem>();

System.out.println(searchStateName);
//get access to the binding context and binding container at runtime
BindingContext bctx = BindingContext.getCurrent();
BindingContainer bindings = bctx.getCurrentBindingsEntry();
//set the bind variable value that is used to filter the View Object
//query of the suggest list. The View Object instance has a View
//Criteria assigned
OperationBinding setVariable = (OperationBinding) bindings.get("setBind_StateName");
setVariable.getParamsMap().put("value", searchStateName);
setVariable.execute();
//the data in the suggest list is queried by a tree binding.
JUCtrlHierBinding hierBinding = (JUCtrlHierBinding) bindings.get("StateView1");


//re-query the list based on the new bind variable values
hierBinding.executeQuery();

//The rangeSet, the list of queries entries, is of type
//JUCtrlValueBndingRef.
List<JUCtrlValueBindingRef> displayDataList = hierBinding.getRangeSet();

for (JUCtrlValueBindingRef displayData : displayDataList){
Row rw = displayData.getRow();
//populate the SelectItem list
selectItems.add(new SelectItem(
(Integer)rw.getAttribute("Id"),
(String)rw.getAttribute("Name")));
}

return selectItems;
}

onSuggest method for City field :

public List onCitySuggest(String searchCityName) {
ArrayList<SelectItem> selectItems = new ArrayList<SelectItem>();

System.out.println(searchCityName);
//get access to the binding context and binding container at runtime
BindingContext bctx = BindingContext.getCurrent();
BindingContainer bindings = bctx.getCurrentBindingsEntry();
//set the bind variable value that is used to filter the View Object
//query of the suggest list. The View Object instance has a View
//Criteria assigned
OperationBinding setVariable = (OperationBinding) bindings.get("setBind_CityName");
setVariable.getParamsMap().put("value", searchCityName);
setVariable.execute();
//the data in the suggest list is queried by a tree binding.
JUCtrlHierBinding hierBinding = (JUCtrlHierBinding) bindings.get("CityView1");


//re-query the list based on the new bind variable values
hierBinding.executeQuery();

//The rangeSet, the list of queries entries, is of type
//JUCtrlValueBndingRef.
List<JUCtrlValueBindingRef> displayDataList = hierBinding.getRangeSet();

for (JUCtrlValueBindingRef displayData : displayDataList){
Row rw = displayData.getRow();
//populate the SelectItem list
selectItems.add(new SelectItem(
(Integer)rw.getAttribute("Id"),
(String)rw.getAttribute("Name")));
}

return selectItems;
}

Once after this, we could bind this to the af:autoSuggestBehavior's suggestedItems property

<af:inputText value="#{bindings.CountryId.inputValue}" label="#{bindings.CountryId.hints.label}"
columns="#{bindings.CountryId.hints.displayWidth}"
autoSubmit="true"
shortDesc="#{bindings.CountryId.hints.tooltip}" id="it3">
<af:autoSuggestBehavior suggestedItems="#{viewScope.AutoSuggestBean.onCountrySuggest}"/>
</af:inputText>
<af:inputText value="#{bindings.StateId.inputValue}" label="#{bindings.StateId.hints.label}"
columns="#{bindings.StateId.hints.displayWidth}"
autoSubmit="true" partialTriggers="it3"
shortDesc="#{bindings.StateId.hints.tooltip}" id="it4">
<af:autoSuggestBehavior suggestedItems="#{viewScope.AutoSuggestBean.onStateSuggest}"/>
</af:inputText>
<af:inputText value="#{bindings.CityId.inputValue}" label="#{bindings.CityId.hints.label}"
columns="#{bindings.CityId.hints.displayWidth}"
autoSubmit="true" partialTriggers="it4"
shortDesc="#{bindings.CityId.hints.tooltip}" id="it5">
<af:autoSuggestBehavior suggestedItems="#{viewScope.AutoSuggestBean.onCitySuggest}"/>
</af:inputText>

Finally, our output would be

output1.jpg

output2.jpg

output3.jpg

Monday Jan 14, 2013

Display Holiday Name in af:calendar

When using af:calendar component in the realtime applications, there is a frequent requirement to display the holdays in it. In this article, we will see how to achieve that.

After setting the environment, we would be expecting the output as shown in the below image.

finalOutput.jpg

Assuming we already have a page with calendar, the individual date level customizations can be done by using DateCustomizer.

For this, we would create a custom class that extends DateCustomizer class.

public class MyDateCustomizer extends DateCustomizer{
public String format(Date date, String key, Locale locale, TimeZone tz)
{
// For illustrative purpose
// Hashmap holding the holiday list
HashMap holidays = new HashMap();
holidays.put(new Date("25-Dec-2012"), "Christmas");
holidays.put(new Date("01-Jan-2013"), "New Year");

if ("af|calendar::month-grid-cell-header-misc".equals(key))
{
return holidays.get(date)!=null?holidays.get(date).toString():null;

}

return null;

}

}

As per the tag doc, following keys are passed to the format method.

  • "af|calendar::day-header-row"
  • "af|calendar::list-day-of-month-link"Year's Day".
  • "af|calendar::list-day-of-week-column"
  • "af|calendar::month-grid-cell-header-day-link"
  • "af|calendar::month-grid-cell-header-misc"
  • "af|calendar::week-header-day-link"

So, in the above code, we return the actual holiday name for the "af|calendar::month-grid-cell-header-misc" key.

In order to use the new DateCustomizer in our calendar, we create an instance of it in the backing bean and bind it to the calendar's dateCustomizer property.

Bean Code :

public class CalendarBean {
private MyDateCustomizer holidays = new MyDateCustomizer(); ;

public CalendarBean() {
}


public void setHolidays(MyDateCustomizer holidays) {
this.holidays = holidays;
}

public MyDateCustomizer getHolidays() {
return holidays;
}
}


Page Source :

<af:calendar id="c1" dateCustomizer="#{viewScope.CalendarBean.holidays}"/>

If needed, the text can be styled to be displayed in different color (as shown in the image at the top). For this, we can have a styleclass in the css

af|calendar::month-grid-cell-header-misc
{
background-color: Yellow;
}

Tuesday Jul 17, 2012

Interpret af:query's queryEvent and display popup to end user using QueryListener

Found an interesting question on OTN. Based on the question, wired a usecase to try out.

Usecase : Show a warning to user when they try to search the records (af:query component), without specifying a criteria / a wild card "%". I.e, when the user tries to query the entire table, show a warning that querying all the records would take some time.

There are three phases in implementing this usecase.

1. Interpret the query event and get the query criteria.
2. Show the popup.
3. Process the interpreted query based on the outcome of the popup.

Before proceeding with the implementation, we'll create a page for assumption.
a. Page contains a af:query component with a resultant table / read-only table.
b. Has a popup to be shown to the end user.
c. Bound to a bean.

We'll now implement it phase by phase.

First of all, we'll create couple of attributes in the bean and generate accessors to them.
    private RichTable empTable;
    private boolean warnUser=true;
   // Set the default queryListener property value of the af:query component
   // for mexpr.
   private String mexpr = "#{bindings.ImplicitViewCriteriaQuery.processQuery}"; 
    private QueryEvent qEvt;

    public void setEmpTable(RichTable empTable) {
        this.empTable = empTable;
    }

    public RichTable getEmpTable() {
        return empTable;
    }



Now, we need to trap the query event of the af:query component to perform the desired task. We can add a queryListener and bind it to the af:query component

  public void processQuery(QueryEvent queryEvent) {
        // store the queryEvent in a bean attribute, to be used in another method.
        qEvt = queryEvent;
        // Reset the flag. This flag would be used to check if the system has to 
        //raise the popup or not
        warnUser=false;
        DCBindingContainer bc =   
        (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();   
         // Get the view criteria that would be applied.
         // findExecutableBinding method takes two parameters.
         // id of the searchRegion executable binding
         // criteria for the searchRegion executable binding
         // Ex : <searchRegion Criteria="__ImplicitViewCriteria__"      
         // Customizer="oracle.jbo.uicli.binding.JUSearchBindingCustomizer"
         //         Binds="EmpView1Iterator" id="ImplicitViewCriteriaQuery"/>
       


 ViewCriteria vc = 
JUSearchBindingCustomizer.getViewCriteria((DCBindingContainer)bc.findExecutableBinding("ImplicitViewCriteriaQuery"),"__ImplicitViewCriteria__");


        ViewCriteriaRow vcr = (ViewCriteriaRow)vc.get(0);
        // Some logic to set the flag. Here, checking if the Ename attribute has
        // no value specified / used a wildcard expression ("%").
        for(int i=0;i<vcr.getAttributeNames().length;i++) {
           
 if(vcr.getAttributeNames()[i] == "Ename" &&  
("%".equals(vcr.getAttributeValues()[i]) || 
vcr.getAttributeValues()[i]==null)) 
                warnUser=true;
        }
 
        if(warnUser) 
            showPopup();
        else 
            executeQuery();
 
    }



showPopup and executeQuery are custom methods to show the popup and to process the query respectively.

       public void showPopup(){
        UIViewRoot root = FacesContext.getCurrentInstance().getViewRoot();
        RichPopup popup = (RichPopup) root.findComponent("p1");
        RichPopup.PopupHints hints = new RichPopup.PopupHints();
        popup.show(hints);
    }

  // This method invokes the method expression used by af:query component programatically
    public void executeQuery(){
        processMethodExpression(mexpr, new Object[] {qEvt}, new Class[] {QueryEvent.class}); 
        AdfFacesContext adfFacesContext = AdfFacesContext.getCurrentInstance(); 
        adfFacesContext.addPartialTarget(empTable); 
 
    }

    private Object processMethodExpression(String methodExpression, Object[] parameters, Class[] expectedParamTypes) { 
        FacesContext fctx = FacesContext.getCurrentInstance(); 
        ELContext elctx = fctx.getELContext(); 
        Application app = fctx.getApplication(); 
        ExpressionFactory exprFactory = app.getExpressionFactory(); 
       
 MethodExpression methodExpr = exprFactory.createMethodExpression(elctx,
 methodExpression, Object.class, expectedParamTypes); 
        return methodExpr.invoke(elctx, parameters); 
        }


Now, we need to bind the custom querListener created above to the af:query component

  <af:query id="qryId2" headerText="Search" disclosed="true"
      value="#{bindings.ImplicitViewCriteriaQuery.queryDescriptor}"
      model="#{bindings.ImplicitViewCriteriaQuery.queryModel}"
      queryListener="#{viewScope.QueryBean.processQuery}".....



We are almost there. Now, when we run the page and query for the records by keeping Ename as null (in the query panel), we would get the popup.

Final step is to handle the user action on the popup and then proceed executing the query / to stop it.

For this, we'll create a dialog listener and bind it to popup.

 public void onDialog(DialogEvent dialogEvent) {
        Outcome o = dialogEvent.getOutcome();
        if(o == Outcome.yes) 
            executeQuery();
 
    }

                <af:popup childCreation="deferred" autoCancel="disabled" id="p1">
                    <af:dialog id="d2" type="yesNo" title="Are you sure?"
                               dialogListener="#{viewScope.QueryBean.onDialog}">
                       
 <af:outputText value="It would be time consuming to query for all 
records. Are you sure you want to continue?" id="ot9"/>
                        <f:facet name="buttonBar"/>
                    </af:dialog>
                </af:popup>



Here is how the runtime would be.

step1.jpg



Enter % for Ename and hit the Search button

step2.jpg



Popup with a warning message displayed

step3.jpg


Clicking on Yes / No on the popup performs respective task (perform query / cancel query).

step4.jpg



Wednesday Sep 14, 2011

Paritally restricting user entry in the ADF input text - using Java Script

Recently came across a forum post in which the OP wanted to let the users edit the content of the text field partially.

https://forums.oracle.com/forums/thread.jspa?forumID=83&threadID=2259832

Here is an example. Let us assume the input text contains the following text. "You can edit the content inside { this }". In this, users should be able to edit only the content inside { }. I.e Only "this" should be editable.

To achieve this, we can use a java script method, that tracks the cursor position and ignore the user edits if the cursor position is not between the curly braces. After which, the method would be used in the client listener for the input text.

Example code snippet.



<af:inputText label="Partial Editable Text Item" 
id="it1" value="You can edit the content inside { this }" clientComponent="true" >
<af:clientListener  type="keyPress" method="validateValue"  /> 
           
</af:inputText>
<af:resource type="javascript">
               function validateValue(evt){
                       var inputTxt=document.getElementById('it1::content');
                       var startPos = inputTxt.value.indexOf("{");
                       var endPos = inputTxt.value.indexOf("}");
                       var cursorPos = inputTxt.selectionStart;
                          if (cursorPos &lt; startPos+2 || cursorPos > endPos-1) { 
                                   alert("Cannot Edit");
                                   evt.cancel();
                          }
 }
</af:resource>

More complex example by Frank Nimphius http://blogs.oracle.com/jdevotnharvest/entry/get_social_security_numbers_right
Note : Tested the above snippet successfully in Mozilla Firefox and IE 9.
About

Tips & Tricks from Arun on JDev ADF, Forms, SQL & PL/SQL.

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