Monday, 4 April 2016

af query search panel customization & some handy tips(ADF 12.2.1)

Topics covered

1. Passing user entered values in search panel to a DB package before executing a search.
2. How to get 2 date values entered using between operator in advanced mode for further processing.
3. How to hide Advanced button from af:query search panel?
4. How to hide Match All and Any radio option in af:query panel?
5. How to restrict operators for search fields in advanced mode in af:query panel  if field is part of view criteria?
6. How to restrict operators for search fields in advanced mode in af:query panel if field is not part of view criteria?
7. How to hide “Add Fields” button in af:query search panel?
8. Code snippet to handle "on-before" and "on-after" as well handling null values in default between operator


ADF Search Query Panel (af:query)

    The af:query component displays a search panel with various elements and it executes a query based on search criteria . In this document, I will explain possible customizations and some handy tips.
To achieve basic functionality of search panel, all we have to do is to drag and drop “All Queriable Attributes” (Named Criteria) from Data control panel. If we want specific criteria then we create view criteria on the particular view and will drag and drop on to a page as a query panel.
Now I will explain different use cases where you have to do customization and achieve the functionality.

1: Suppose, I want to pass user entered values in search panel to a DB package before executing a search. Or to put it in other way, I need to hold the values for some business logic before executing search.

Solution: Customize the queryListener of your af:query panel.

When you drag and drop view criteria to a page as a query panel, by default query listener of your af:query is queryListener="#{bindings.<vo>CriteriaQuery.processQuery}"  .

Change this and execute processQuery operation from your bean method
Ex:
#{backingBeanScope.Test BackingBean.customQueryListener}

Now logic for your cutomQueryListener method is as follows

 public void cutomQueryListener(QueryEvent  queryEvent){
 String EmployeeId = null;
        // Query Event is delivered when a query action is triggered
        QueryDescriptor qd = queryEvent.getDescriptor();
     
        // This line will represent group of criterion objects
        ConjunctionCriterion conCrit = qd.getConjunctionCriterion();
     
        //access the list of all search fields
        List<Criterion> criterionList = conCrit.getCriterionList();

        for (Criterion criterion : criterionList) {
            AttributeDescriptor attrDescriptor =  ((AttributeCriterion)criterion).getAttribute();

            if (attrDescriptor.getName().equalsIgnoreCase("EmployeeId")) { // EmployeeId is one of the query items in the search panel
             EmployeeId  =  (String)((AttributeCriterion)criterion).getValues().get(0);
           
            //This is how we will access the query field
             System.out.println("EmployeeId :" + EmployeeId );

            }

Note: At this moment, I have value entered in EmployeeId field. So, I can do whatever validation on the user entered value or pass it to package call before executing search query.

//Execute the query Listener using EL. This will execute the query component .If u see the exp , this was initially applied to QueryListener.. Later we assigned QueryListener to our custom method.
   
   invokeMethodExpression("#{bindings.VOCriteriaQuery.processQuery}", queryEvent);
     
    }
 
      //helper method to execute the QueryListener EL

       private void invokeMethodExpression(String expr, QueryEvent queryEvent) {
        FacesContext fctx = FacesContext.getCurrentInstance();
        ELContext elContext = fctx.getELContext();
        ExpressionFactory eFactory =
            fctx.getApplication().getExpressionFactory();
        MethodExpression mexpr =
            eFactory.createMethodExpression(elContext, expr, Object.class,
                                            new Class[] { QueryEvent.class });
        mexpr.invoke(elContext, new Object[] { queryEvent });
   }

 2: How to get 2 date values entered using between operators in advanced mode for further processing.

Say for Example, we have “Active Date” field in search panel and we want to get “FromActiveDate” and “ToActiveDate”  from this search field. In this case, we need to find whether search panel is in advanced mode or basic mode.

You can achieve this by using below line of code

qdesc.getUIHints().get(QueryDescriptor.UIHINT_MODE).equals(QueryDescriptor.QueryMode.ADVANCED)

Complete code logic is as shown below

if (attrDescriptor.getName().equalsIgnoreCase("ActiveDate")) {

                // Checking whether it is advanced mode
                if (qdesc.getUIHints().get(QueryDescriptor.UIHINT_MODE).equals(QueryDescriptor.QueryMode.ADVANCED)) {
                 
                    Map<AttributeCriterion, Operator> changedAttrs = new HashMap<AttributeCriterion, Operator>();
                    AttributeCriterion ac = (AttributeCriterion) criterion;
                    Operator operator = ac.getOperator();
                    if ("BETWEEN".equalsIgnoreCase(operator.getValue().toString())) {
                        oracle.jbo.domain.Date effFromDate1 =
                            (oracle.jbo.domain.Date) ((AttributeCriterion) criterion).getValues().get(0);

                        if (null != effFromDate1) {
                         
                            effectiveFromDate = effFromDate1.getValue();
                        }


                        oracle.jbo.domain.Date effToDate1 =
                            (oracle.jbo.domain.Date) ((AttributeCriterion) criterion).getValues().get(1);

                        if (null != effToDate1) {
                         
                            effectiveToDate = effToDate1.getValue();
                        }
                    } else {
                        oracle.jbo.domain.Date effFromDate1 =
                            (oracle.jbo.domain.Date) ((AttributeCriterion) criterion).getValues().get(0);

                        if (null != effFromDate1) {
                         
                            effectiveFromDate = effFromDate1.getValue();
                        }
                    }


                    //Basic mode:
                } else {
                    oracle.jbo.domain.Date effectiveFromDate1 =
                        (oracle.jbo.domain.Date) ((AttributeCriterion) criterion).getValues().get(0);
                    if (null != effectiveFromDate1) {
                        effectiveFromDate = effectiveFromDate1.getValue();
                    }

                }

3. How to hide Advanced button from af:query

From the af:Query properties, set  property values as modeChangeVisible="false"
Select af:query in the page, and from the properties window, under appearance section you can find the modeChangeVisible property as shown below screenshot






4. How to hide Match All and Any radio option in af:query panel

1. Edit View Criteria
2. In the Criteria UI hints – Un check the Show Match all  and Match Any check box




Note: To make  required and put **  for the af:query panel attributes make criteria item as required for * and selectively required for **

5. How to restrict operators for search fields in advanced mode in af:query panel

1. Edit view criteria
2. Under Criteria Definition tab, select criteria item
3. Go to “Custom Operators” tab in the bottom
4. Remove operators which you do not want to show for particular attributes in advanced mode



6. What if field is not part of your view criteria and still you want to restrict the operators.
Approach to be followed


In the above case you still achieve like the way we used to do in 11g by using
<CompOper>

To make our life easier,
1) first add attribute to view criteria,
2)remove unnecessary operators form custom operators
3)Go to source mode, copy CompOper from view criteria item and put it some where for reference
4)remove the attribute from view criteria
5)Go to source mode, find the attribute then copy the CompOper properties which you copied in step3


Then go to source mode and then find out the attribute for which you want to restrict the operators then use <CompOper>
for Ex, In below example, I have restricted all the operators except equals for string attribute

<ViewAttribute
    Name="Status"
    IsPersistent="false"
    PrecisionRule="true"
    Precision="1"
    Type="java.lang.String"
    ColumnType="VARCHAR2"
    AliasName="STATUS"
    Expression="STATUS"
    SQLType="VARCHAR"
    LOVName="LOV_Status">
    <DesignTime>
      <Attr Name="_DisplaySize" Value="1"/>
    </DesignTime>
    <CompOper
      Name="Status_Oper"
      ToDo="-1"
      Oper="&lt;"
      MinCardinality="1"
      MaxCardinality="1"/>
    <CompOper
      Name="Status_Oper1"
      ToDo="-1"
      Oper="&lt;="
      MinCardinality="1"
      MaxCardinality="1"/>
    <CompOper
      Name="Status_Oper2"
      ToDo="-1"
      Oper="&lt;>"
      MinCardinality="1"
      MaxCardinality="1"/>
    <CompOper
      Name="Status_Oper3"
      ToDo="-1"
      Oper=">"
      MinCardinality="1"
      MaxCardinality="1"/>
    <CompOper
      Name="Status_Oper4"
      ToDo="-1"
      Oper=">="
      MinCardinality="1"
      MaxCardinality="1"/>
    <CompOper
      Name="Status_Oper5"
      ToDo="-1"
      Oper="BETWEEN"
      MinCardinality="1"
      MaxCardinality="1"/>
    <CompOper
      Name="Status_Oper6"
      ToDo="-1"
      Oper="CONTAINS"
      MinCardinality="1"
      MaxCardinality="1"/>
    <CompOper
      Name="Status_Oper7"
      ToDo="-1"
      Oper="DOESNOTCONTAIN"
      MinCardinality="1"
      MaxCardinality="1"/>
    <CompOper
      Name="Status_Oper8"
      ToDo="-1"
      Oper="ENDSWITH"
      MinCardinality="1"
      MaxCardinality="1"/>
    <CompOper
      Name="Status_Oper9"
      ToDo="-1"
      Oper="ISBLANK"
      MinCardinality="1"
      MaxCardinality="1"/>
    <CompOper
      Name="Status_Oper10"
      ToDo="-1"
      Oper="ISNOTBLANK"
      MinCardinality="1"
      MaxCardinality="1"/>
    <CompOper
      Name="Status_Oper11"
      ToDo="-1"
      Oper="NOTBETWEEN"
      MinCardinality="1"
      MaxCardinality="1"/>
    <CompOper
      Name="Status_Oper12"
      ToDo="-1"
      Oper="STARTSWITH"
      MinCardinality="1"
      MaxCardinality="1"/>
    <Properties>
      <SchemaBasedProperties>
        <CONTROLTYPE
          Value="choice"/>
      </SchemaBasedProperties>
    </Properties>
  </ViewAttribute>

7. How to hide “Add Fields” button in af:query search panel?

In the, jsff(or in your page), within af:query component add a facet footer and specify width and height as 1 as shown below
<af:query id="qryId1" headerText="SEARCH"
                                          disclosed="true"
                                          value="#{bindings.TestVOCriteriaQuery.queryDescriptor}"
                                          model="#{<model bidning value> }"
                                          queryListener="#{<query listener value>}"
                                          queryOperationListener="#{bindings. VOCriteriaQuery.processQueryOperation}"
                                          maxColumns="2" rows="3" fieldWidth="30%" labelWidth="70%"
                                          resultComponentId="::at1:_ATp:t1">
                                    <f:facet name="footer">
                                        <af:spacer width="1" height="1" id="s3"/>
                                    </f:facet>
                                </af:query>

8.  Code snippet to handle "on-before" and "on-after" as well handling null values in default between operator

            if (attrDescriptor.getName().equalsIgnoreCase("ActiveDate")) {



                 
                    Map<AttributeCriterion, Operator> changedAttrs = new HashMap<AttributeCriterion, Operator>();
                    AttributeCriterion ac = (AttributeCriterion) criterion;
                    Operator operator = ac.getOperator();
                    if ("BETWEEN".equalsIgnoreCase(operator.getValue().toString())) {
                        oracle.jbo.domain.Date effFromDate1 =
                            (oracle.jbo.domain.Date) ((AttributeCriterion) criterion).getValues().get(0);

                        if (null != effFromDate1) {
                         
                            effectiveFromDate = effFromDate1.getValue();
                        }


                        oracle.jbo.domain.Date effToDate1 =
                            (oracle.jbo.domain.Date) ((AttributeCriterion) criterion).getValues().get(1);

                        if (null != effToDate1) {
                         
                            effectiveToDate = effToDate1.getValue();
                        }
                        String op = null;
                                                            String opDate = null;
                                                            Object val = null;
                                                            List<Object> list = (List<Object>)ac.getValues();
                                                            if (list.get(0) == null && list.get(1) != null) {
                                                                                op = "<=";
                                                                                opDate = "ONORBEFORE";
                                                                                val = "1900-01-01";
                                                                                list.set(0, val);
                                                            } else if (list.get(1) == null && list.get(0) != null) {
                                                                                op = ">=";
                                                                                opDate = "ONORAFTER";
                                                                                val = "9999-01-01";
                                                                                list.set(1, val);
                                                            }
                                                            if (op != null) {
                                                                changedAttrs.put(ac, operator);
                                                                for (Operator o : ac.getAttribute().getSupportedOperators()) {
                                                                    if (o.getValue().toString().equalsIgnoreCase(op) || o.getValue().toString().equalsIgnoreCase(opDate)) {
                                                                        operator = o;
                                                                        break;
                                                                    }
                                                                }
                                                                ac.setOperator(operator);
                                                            }
                    } else {
                        oracle.jbo.domain.Date effFromDate1 =
                            (oracle.jbo.domain.Date) ((AttributeCriterion) criterion).getValues().get(0);

                        if (null != effFromDate1) {
                         
                            effectiveFromDate = effFromDate1.getValue();
                        }
                    }


}



References:

1. ADF code corner: af:query component field validation by Frank Nimphius
2. http://www.jobinesh.com/2013/01/prart-2-hiding-unwanted-operators-in.html - By Jobinesh Purushothaman

5 comments:

  1. Hi Kotresh,
    Request you to kindly look into issue mentioned in below thread and provide suitable solution:
    https://community.oracle.com/message/13981922#13981922

    ReplyDelete
  2. Hi,
    You mentioned issues occurs only with IE, so it looks like browser issue, i seen similar thread
    https://community.oracle.com/thread/3644936

    Looks like there is some incompatible issue with ADF 12c and IE11.
    Thanks

    ReplyDelete
  3. Hi Kotresh,

    I want to create RichInputComboboxListOfValues using java and AttributeDescriptor.

    I am doing something below:

    RichInputComboboxListOfValues comboBox = new RichInputComboboxListOfValues();
    QueryDescriptor qd = (QueryDescriptor)Utils.evaluateEL("#{bindings.EmployeesVCQuery.searchRegionBinding.queryDescriptor}");
    ConjunctionCriterion conCrit = qd.getConjunctionCriterion();
    List criterionList = conCrit.getCriterionList();
    for(Criterion criterion : criterionList){
    AttributeCriterion attributeCriterion = (AttributeCriterion)criterion;
    AttributeDescriptor attrDescriptor = ((AttributeCriterion)criterion).getAttribute();
    if(attrDescriptor.getComponentType().equals("inputComboboxListOfValues")){
    comboBox.setModel((ListOfValuesModel)attributeCriterion.getModelList().get(0)); //Setting model
    }
    }

    I am adding comboBox as a child of some pgl.
    When I am expanding list in comboBox, getting below exception.

    at weblogic.work.ExecuteThread.execute(ExecuteThread.java:256)
    at weblogic.work.ExecuteThread.run(ExecuteThread.java:221)

    java.lang.NullPointerException
    at oracle.jbo.uicli.binding.JUCtrlValueBinding.getAttributeDef(JUCtrlValueBinding.java:590)
    at oracle.jbo.uicli.binding.JUCtrlValueBinding.getAttributeDef(JUCtrlValueBinding.java:2783)
    at oracle.adfinternal.view.faces.model.binding.FacesCtrlLOVBinding.getDisplayMode(FacesCtrlLOVBinding.java:788)
    at oracle.adfinternal.view.faces.model.binding.FacesCtrlLOVBinding.getItems(FacesCtrlLOVBinding.java:950)

    Any suggestion as why model doesn't work using bean?

    ReplyDelete
  4. Thanks for the solution you have provided "How to hide “Add Fields” button in af:query search panel"

    ReplyDelete
  5. Hi Kotresh,

    Can you please tell how to hide Collapse Search on af:query and I want the query panel to be displayed all the time and don't need collapse/Expand Search functionality.

    Thanks,
    Vishal Kumar

    ReplyDelete