In this blog article, I show how to use a multi-select component as a filter in a table. The sample is based on an older sample from Frank Nimphius (98. How-to use multi select components in table filters). The sample was built for JDev 11g R1 and R2.
It works using 12c too, but you get deprecation warnings after the migration. A user on the JDeveloper & ADF forum asked if I could provide a sample running in 12c without the deprecation warnings.
I will only show how to rewrite the bean method which is called when the user enters one or more values in the filter. The remaining part of the original sample works without a change in 12c.
To better understand what I’m talking about I show some images from the original blog:
The image above shows the sample table. Below we see the multi-select component to filter for multiple departments:
Please read the original blog entry to understand how to build the UI. The remaining part of this blog covers how to build the custom query listener method.
Custom Query Listener
In the original sample the two methods
...
Map _criteriaMap = fqd.getFilterCriteria();
...
fqd.setFilterCriteria(_criteriaMap);
are used which produce deprecation warnings in 12c
Starting from JDev 12.1.3 you can’t use the criteriaMap from the FilterableQueryDescriptor. Instead, you have to first get the ConjunctionCriterion from the FilterableQueryDescriptor and get the map of Criterion from it. The map holds the filter criteria entered by the user.
As you don’t use the criteria directly, you can’t set it back after generating the filter fro the multi-select. You work with the Criterion instead.
The new Method looks like
/**
* Custom Query Listener.
* Applies af:selectMany choice values to the table filter criterion
* @param queryEvent
*/
public void onEmployeeTableQuery(QueryEvent queryEvent) {
//user selected values
ArrayList<Object> departmentIdArray = null;
FilterableQueryDescriptor fqd = (FilterableQueryDescriptor) queryEvent.getDescriptor();
//current criteria
ConjunctionCriterion conjunctionCriterion = fqd.getFilterConjunctionCriterion();
Map<String, Criterion> criterionMap = conjunctionCriterion.getCriterionMap();
Criterion criterion = criterionMap.get("DepartmentId");
//Translate DepartmentId array list to OR separate list of values
StringBuffer deptIdFilterString = new StringBuffer();
AttributeCriterion adfcriterion = null;
// flag we set only if the DepartmentId filter is set (to reset the selection later)
boolean flagDepIdFilter = false;
if (criterion != null) {
adfcriterion = (AttributeCriterion) criterion;
Object object = adfcriterion.getValue();
if (object != null) {
flagDepIdFilter = true;
departmentIdArray = (ArrayList<Object>) object;
for (int argIndex = 0; argIndex < departmentIdArray.size(); argIndex++) {
//You need to know what is the underlying data type you are dealing
//with for the attribute. If you are on 11gR1 (11.1.1.x) then this
//type is jbo.domain.Number for numeric attributes.
//
//If you are on 11g R2 (11.1.2.x) this could be oracle.jbo.domain.Number,
//Integer or BigDecimal. If you use 11g R2, check the View Object for the
//attribute data type
if (argIndex == 0) {
//first argument has no OR
//this sample used oracle.jbo.domain.Number for the
//DepartmentId attribute
Number departmentId = (Number) departmentIdArray.get(argIndex);
deptIdFilterString.append(departmentId.toString());
} else {
//any subsequent argument is OR'ed together
deptIdFilterString.append(" OR ");
Number departmentId = (Number) departmentIdArray.get(argIndex);
deptIdFilterString.append(departmentId.toString());
}
}
//for some reasons, if in a single value select case, the
//filter breaks and an error message is printed that the
//String representation of the single value isn't found in
//the list. The line below fixes the problem for filter values
//that are positive numbers
deptIdFilterString.append(" OR -1");
String departmentIds = deptIdFilterString.toString();
adfcriterion.setValue(departmentIds);
fqd.setCurrentCriterion(adfcriterion);
}
}
// preserve default query listener behavior
//#{bindings.allEmployeesQuery.processQuery}
FacesContext fctx = FacesContext.getCurrentInstance();
Application application = fctx.getApplication();
ExpressionFactory expressionFactory = application.getExpressionFactory();
ELContext elctx = fctx.getELContext();
MethodExpression methodExpression =
expressionFactory.createMethodExpression(elctx, "#{bindings.allEmployeesQuery.processQuery}", Object.class,
new Class[] { QueryEvent.class });
methodExpression.invoke(elctx, new Object[] { queryEvent });
//restore filter selection done by the user. Note that this
//needs to be saved as an ArrayList
if (flagDepIdFilter) {
adfcriterion.setValue(departmentIdArray);
fqd.setCurrentCriterion(adfcriterion);
}
}
From the FilterableQueryDescriptor we get the ConjunctionCriterion and from this the map of Criterion. This map holds all filter values entered by the user in the filter of the table. We retrieve the one for the ‘DepartmentId’ and check if the value for it is not null. In this case, the criterion holds an array of the selected DepartmentId. From this array, we build a new string where we use the ‘OR’ operator to concatenate the array values.
Once this string is built, we set it back to the Criterion and execute the original query listener
You can download the sample from GtHubBlogMultiSelectComponentFilterTable for inspection and/or testing. The sample was built using JDev 12.2.1.3 and uses the HR DB schema.