Thursday, June 28, 2012

Table Custom Filter


Hello, Everyone;
In this post I would like to talk about custom filters.
as you all know ADF LOVs are easy to use and develop compared to other frameworks, but when used in tables you have to be careful if you want  add filtering to your tables if they have LOV for one of their attributes.
even if the table is configured to display SelectOneChoice and displays the desired value (not the foreign key) you will have to enter the foreign key to filter the table .
for example:
in the HR schema you have countries,region tables.




















in the above table you can't enter Asia as value for the filter or you will get
but if you enter 3

to solve this problem we add a custom filter to our table.

the problem: add custom filter to ADF table.

note:the following are just the steps to create the filter, it's assumed that the LOV has already been configured on the VIEW Object. 

note: I will be using  JDEV: Studio Edition Version 11.1.1.3.0.

the use case: I will be using the hr schema tables: countries, regions to create a table that lists all the countries but I should be able to filter it by the region.

the solution:
1)create new page and add a table(read only or not is up to the use case , but here I will use read only) to it by dragging from the data control palette .
2)make sure you selected the table, then from its properties press the edit component definition button:
and make sure the filtering checkbox is checked and the column that has LOV configured has the select one choice selected as the component to use
3)since I only want read only table, I will modify the select one choice to readonly.(which will make it appear as if you are using textoutput).

* now I will create the select one choice( SOC ) for the filter.
note: ideally speaking, I should create a list binding in the binding page and have the SOC bound to it.
but the value for the selected item will be its index rather than its id, after looking around on the internet  I found that if the property valuePassThru of the SOC is set to true the Soc value should be the id not the index. but apparently this does't work in the release I'm using. so I had  to do it in a roundabout way.

4)go to the binding tab of the page, add a new iterator to the executable binding for the region

choose regionview  as the source:

5)from the structure palette go to table-> region id column->column facets->filter, then add SOC
from the insert select one choice dialog remove the label and leave the rest as is.

6)select the added SOC, then from its property inspector use the expression builder to set its value property
to #{vs.filterCriteria.RegionId}
7)for the SOC delete the SelectItems from the structure palette then add a ForEach to the SOC. after that, add adf faces SelectItem to the ForEach then go to the source tab of the page and modify the added components to:

               <af:selectOneChoice id="soc2"
                                    value="#{vs.filterCriteria.RegionId}">
                  <af:forEach items="#{bindings.RegionsView1Iterator.allRowsInRange}"
                              var="item">
                    <af:selectItem label="#{item.regionName}" id="si2"
                                   value="#{item.regionId}"/>
                  </af:forEach>
                </af:selectOneChoice>
            note: you might want to add an item that shows all the rows, to do that add the
                following:
                              <af:selectItem value="" label="selectItem 1" id="si3"/>
                 before or after the ForEach inside the SOC.
8)make sure that you have generated the java impl class for the region view.

9)run and test the page.

the above should be enough to work but you might want to have the table filtered as soon as you select the region not after you press the Enter button after selecting it.
to do that you have to use javascript.
to do that edit the source to this(inside the column of the region id):
<f:facet name="filter">
    <af:resource type="javascript">
    //TODO add support for web browsers that use webkit 
        function filter1(evt){
            var component=document.getElementById(evt.getSource().getClientId());
             if(document.createEvent){ 
             //For FireFox 
             var event=document.createEvent('KeyboardEvent');
             event.initKeyEvent('keydown', true, true, window, false,false, false, false, 13, 0);
              component.dispatchEvent(event); 
               }else{
               //For I.E 
                var event2=document.createEventObject(); event2.keyCode=13;
                component.fireEvent("onkeydown",event2); 
                } 
              }
      </af:resource>
 <af:selectOneChoice id="soc2"
         value="#{vs.filterCriteria.RegionId}"
          valueChangeListener="#{CustomTableFilter.CustomTableFilter}">
  <af:clientListener type="valueChange" method="filter1"/>
        <af:selectItem label="All" value="" id="si2"/>
         <af:forEach items="#{bindings.RegionsView1Iterator.allRowsInRange}"
                var="item">
           <af:selectItem label="#{item.regionName}" value="#{item.regionId}"  id="si4"/>
         </af:forEach>
   </af:selectOneChoice>
</f:facet>


note : apparently web browsers that use webkit(like chrome) have a bug that prevents them from using this code correctly.

Tuesday, June 26, 2012

Getting the value of list

Hello, Everyone;
a while age I had to use select one choice to filter a record set then display in a table.
there are multiple approaches that could be used here but what I ended up using was:
list binding+select one choice+valueChangeEvent.

the problem that I faced was how to get the selected value?
Some people said use the binding property to bind to the backing bean then set the valuePassThru =true so that getValue() return wanted value but for some reason or another it did not work.

eventually fund some workarounds to be inserted into the backing bean's valueChangeEvent method


1)
public void DeptListChangeEvt(ValueChangeEvent valueChangeEvent) {
     
BindingContainer container=BindingContext.getCurrent().getCurrentBindingsEntry();
 JUCtrlListBinding list =  (JUCtrlListBinding)container.get("DepartmentName");

valueChangeEvent.getComponent().processUpdates(FacesContext.getCurrentInstance());
      System.out.println(((Row)list.getSelectedValue()).getAttribute(1));
    }




2)
public void DeptListChangeEvt(ValueChangeEvent valueChangeEvent) {


       BindingContainer container=BindingContext.getCurrent().getCurrentBindingsEntry();
       DCIteratorBinding iter =container.findIteratorBinding("departmentLOVIterator");


       Row row=iter .getRowAtRangeIndex((Integer)(valueChangeEvent.getNewValue());
        System.out.println(row.getAttribute(0));
      
    }

3)

public void DeptListChangeEvt(ValueChangeEvent valueChangeEvent) {

       
       RowSetIterator rowSetIt=  list.getListRowSetIterator();
        Row x= rowSetIt.getRowAtRangeIndex((Integer)valueChangeEvent.getNewValue());
     
        System.out.println("list iterator");
        System.out.println(x.getAttribute(0));
      
    }

if you simply want to get the value aproach 1 is good, but if there is some logic that depends on the the new and old value then you use either 2 or 3.




started working with ADF

Hello everyone, 
Since I started working with the ADF framework ;I decided to write a blog documenting my adventures with it.
Every now and then I will try to add a new article describing some of what I've been able to do with it

Regard,
Ahmad