Pages

Friday, February 6, 2009

Display detail records in a dynamic ADF FormLayout

Sometimes you have to make a CRUD page on master detail tables which don't have fixed columns like the tables in the HR demo schema. The detail table has for example only a foreign key to the master table and a type and value column. The detail values are now stored vertically and not horizontal in columns. In this blog I will show how to make a dynamic custom form with inputtexts for each master.
Here is an picture of the category and category_items table I use in this blog. In this example I got for example a category U2 with the some items records like Style and Country and a category week with the weekdays as items.
I will use an local EJB as model because this gives me the Category and CategoryItems classes which I can use in the backing bean and the EJB session bean has a nice merge method which I can use when I send back the changed category.
On the EJB Session bean I created an ADF datacontrol so I can call method actions in the page definition and these methods actions will fill the method iterators.

In the left site of the page I will display all the categories and on the right I have a dynamic task flow region. You can click on a Id this will display the detail form.

When I click on my car then I will call an method action to retrieve this category class. With the items of this category I will open the category items task flow and use af:forEach to display all the items

If I select an other category I first remove the previous inputtext components and display the new items
When I change an item I can press the save button. This will call the merge method action and passes the category with the changed items to the ejb session bean, EJB will update the items values.

Here is the ddl of my examples tables.


create table CATEGORY
(
ID NUMBER not null,
NAME VARCHAR2(30) not null
)
;
alter table CATEGORY
add constraint CATEGORY_PK primary key (ID);

insert into CATEGORY (ID, NAME)
values (1, 'U2');
insert into CATEGORY (ID, NAME)
values (2, 'SEAT LEON');
insert into CATEGORY (ID, NAME)
values (3, 'WEEK');
commit;

create table CATEGORY_ITEMS
(
CAT_ID NUMBER(4) not null,
NAME VARCHAR2(40) not null,
VALUE VARCHAR2(40) not null
)
;
alter table CATEGORY_ITEMS
add constraint CATEGORY_ITEMS_PK primary key (CAT_ID, NAME);
alter table CATEGORY_ITEMS
add constraint CATEGORY_ITEMS_CATEGORY_FK1 foreign key (CAT_ID)
references CATEGORY (ID);

insert into CATEGORY_ITEMS (CAT_ID, NAME, VALUE)
values (1, 'Style', 'Pop');
insert into CATEGORY_ITEMS (CAT_ID, NAME, VALUE)
values (1, 'Country', 'Ireland');
insert into CATEGORY_ITEMS (CAT_ID, NAME, VALUE)
values (1, 'Albums', 'War,Rattle and hum');
insert into CATEGORY_ITEMS (CAT_ID, NAME, VALUE)
values (2, 'Color', 'Black');
insert into CATEGORY_ITEMS (CAT_ID, NAME, VALUE)
values (2, '4WD', 'No');
insert into CATEGORY_ITEMS (CAT_ID, NAME, VALUE)
values (2, 'HorsePower', '140');
insert into CATEGORY_ITEMS (CAT_ID, NAME, VALUE)
values (2, 'Transmission', 'Automatic');
insert into CATEGORY_ITEMS (CAT_ID, NAME, VALUE)
values (3, 'day1', 'Monday');
insert into CATEGORY_ITEMS (CAT_ID, NAME, VALUE)
values (3, 'day2', 'Tuesday');
insert into CATEGORY_ITEMS (CAT_ID, NAME, VALUE)
values (3, 'day3', 'Wednesday');
insert into CATEGORY_ITEMS (CAT_ID, NAME, VALUE)
values (3, 'day4', 'Thursday');
insert into CATEGORY_ITEMS (CAT_ID, NAME, VALUE)
values (3, 'day5', 'Friday');
insert into CATEGORY_ITEMS (CAT_ID, NAME, VALUE)
values (3, 'day6', 'Saturday');
insert into CATEGORY_ITEMS (CAT_ID, NAME, VALUE)
values (3, 'day7', 'Sunday');
commit;


My backing which calls the method action and retrieves the Category from the method iterator.

package nl.whitehorses.dynamic.beans;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.faces.event.ActionEvent;

import nl.whitehorses.dynamic.form.model.entities.Category;

import nl.whitehorses.dynamic.form.model.entities.CategoryItems;

import oracle.adf.controller.TaskFlowId;
import oracle.adf.model.BindingContext;
import oracle.adf.model.bean.DCDataRow;
import oracle.adf.model.binding.DCBindingContainer;

import oracle.adf.model.binding.DCIteratorBinding;

import oracle.adf.view.rich.component.rich.layout.RichPanelFormLayout;

import oracle.adfinternal.view.faces.model.binding.FacesCtrlActionBinding;

import oracle.jbo.Row;

public class Main {

private Long catId;
private DCBindingContainer dc;
private Category category;
private String emptyTaskFlowId = "/WEB-INF/empty-task-flow-definition.xml#empty-task-flow-definition";
private String categoryTaskFlowId = "/WEB-INF/category-task-flow-definition.xml#category-task-flow-definition";
private String taskFlowId;
List catItemList;
private RichPanelFormLayout categoryItemForm;

public Main() {
dc = (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
}

public void categoryTableLink(ActionEvent actionEvent) {
// Add event code here...

// the category pk is already set
// now get the category
FacesCtrlActionBinding categoryFindByKey = (FacesCtrlActionBinding)dc.getControlBinding("queryCategoryFindByKey");
categoryFindByKey.execute();

// adf executed the method action
// now get the result and put this in the category variable
DCIteratorBinding categoryBinding= (DCIteratorBinding)dc.get("queryCategoryFindByKeyIter");
Row[] categoryRows = categoryBinding.getAllRowsInRange();
for (Row row : categoryRows) {
category = (Category)((DCDataRow)row).getDataProvider();
}


// remove old childern when a other category is selected
if ( categoryItemForm != null ) {
int size = categoryItemForm.getChildren().size();
for ( int i = 0 ; i < size ; i++ ) {
categoryItemForm.getChildren().remove(0);
}
}
catItemList = category.getCategoryItemsList();
// set category taskflow
taskFlowId = categoryTaskFlowId;
}

public void saveCategoryItems(ActionEvent actionEvent) {
// Add event code here...
FacesCtrlActionBinding merge = (FacesCtrlActionBinding)dc.getControlBinding("mergeEntity");
merge.execute();
}

public TaskFlowId getDynamicTaskFlowId() {
if ( taskFlowId == null ) taskFlowId = emptyTaskFlowId;
return TaskFlowId.parse(taskFlowId);
}


public void setCatId(Long catId) {
this.catId = catId;
}

public Long getCatId() {
return catId;
}

public String getCategoryFragment() {
return category.getName();
}

public void setCatItemsList(List catItemList) {
this.catItemList = catItemList;
}

public List getCatItemsList() {
return catItemList;
}

public void setCategoryItemForm(RichPanelFormLayout categoryItemForm) {
this.categoryItemForm = categoryItemForm;
}

public RichPanelFormLayout getCategoryItemForm() {
return categoryItemForm;
}

public void setCategory(Category category) {
this.category = category;
}

public Category getCategory() {
return category;
}
}

and as last the category items jsf page code

<?xml version='1.0' encoding='windows-1252'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
xmlns:af="http://xmlns.oracle.com/adf/faces/rich"
xmlns:f="http://java.sun.com/jsf/core">
<af:subform>
<af:panelHeader text="Category #{MainBean.categoryFragment}">
<af:panelFormLayout binding="#{MainBean.categoryItemForm}">
<af:forEach var="row" items="#{MainBean.catItemsList}">
<af:inputText label="#{row.name}" value="#{row.value}"/>
</af:forEach>
<f:facet name="footer">
<af:commandButton text="Save"
actionListener="#{MainBean.saveCategoryItems}"/>
</f:facet>
</af:panelFormLayout>
</af:panelHeader>
</af:subform>
</jsp:root>


Here is my 11g example workspace

3 comments:

  1. Clear and thorough article, thanks.

    ReplyDelete
  2. thank you ... its was a good example and usefull too

    husam.obaid@gmail.com

    ReplyDelete
  3. Hi Edwin, thanks for your post.

    We also have a vertical design in the database and now we are planning the design of the ADF application for this EAV pattern.

    We are working with JDev 12.1.3. Now, do you still recommend this implementation for a big application with EAV pattern in ADF?

    Exists other way using ADF BC?

    Thank in advance

    ReplyDelete