This topic contains the following sections:
The Oracle Dynamic Tabs Shell page template (also known as the ADF UI Shell) supports a tabbed document interface wherein a user can simultaneously work on different tasks at the same time with a single browser window. By itself the template does not show a reference implementation of how to use it in a real ADF application.
This document is a step by step guide on how to use the ADF UI Shell with an EJB/JPA back end. The end result will allow a user to view, create, and edit data simultaneously in different UI Shell tabs. Through this pattern, you will see how to make effective use of the ADF UI Shell to facilitate a rich user interaction with your underlying reusable ADF task flows. Hopefully, this will also provide inspiration for building your own applications. The technologies used in this pattern are ADF Task Flows, ADF Faces RC, and EJB/JPA The prototype presented herein is based on the HR schema that ships with the Oracle XE Database.
The following screenshots provide an overview of the functionality the application will give the user.
Figure 1:User clicks Jobs link to open the Overview tab within the ADF UI Shell
When the user opens the application, the option to click on the “Jobs” link is presented. When the user clicks this link, the Overview tab is launched within the ADF UI Shell, displaying the list of jobs in a table, based on the job-list-task-flow. From the Overview tab, the user has the option, for each job listed, to view the associated record in read only mode, or edit the record. Clicking one of the Job Id links in the Overview tab will launch a new tab based on the job-details-task-flow.
Figure 2: Detailed display of the selected record from the Overview tab.
The new tab is designed to display the details of the selected record in either read-only or edit mode depending on how the task flow was invoked. In view mode this prevents the user from making inadvertent changes. In this tab the user has two options: edit or done. Clicking Done will close the current tab.
Figure 3: Detailed display of the selected record in edit mode.
Alternatively if the user clicks the Edit button, this sets the form into edit-mode. In edit-mode the user can modify the current record. Thereafter the user has two options: Save and Close; or Cancel.
Figure 4: Modified records marked as dirty will set the tab title to italic.
After modifying any attribute and existing the field, the current tab is marked as dirty (unsaved/modified) and the tab title is set to italic to reflect this fact. Clicking Save and Close updates the record and closes the current tab.
Figure 5:Users can edit a selected record from the Overview tab (Edit), or create a new record from the Overview tab (New)
Alternatively, the user can edit by selecting the record in the table and clicking the Edit button in Overview tab. The Refresh button facilitates synchronizing the list from the database. This is particularly useful if there are some other users who are also working on the same table data. If not, which seems rare in an enterprise context, automated refresh could occur as some users would expect it. This is done via ADF contextual events. However, in the interest of keeping this pattern sufficiently focused, that along with other design choices (e.g., security) was not added.
Figure 6: An asterisk appends the tab title to signify new unsaved data.
Clicking the new button opens the job-details-task-flow as a new tab ready to edit a brand new record. Prior to loading the tab, the form is initialized to have a default title of “Default Job Title”. An asterisk appends the tab title to signify new unsaved data in this tab.
Figure 7: Collapse the splitter for the left navigation area to provide more screen real estate to the tab region.
The user can open multiple tabs of the same task flow definition from the same Jobs table. Further, the user can collapse the splitter for the left navigation area to provide more screen real estate to the tab region. Each tab may have its own state: read-only, edit, or new. The user can change the row selection in the Overview table without affecting the view of any opened tab.
The demonstration you will build uses the following artifacts. Note you will build these step by step in the next sections so this section is included as quick summary to what you will create:
A. JPA Entity Model Objects
Job.java - An entity that was automatically generated by JDeveloper through the Entity-From-Tables wizard
public class Job implements Serializable {
@Id
@Column(name="JOB_ID", nullable = false, length = 10)
private String jobId;
@Column(name="JOB_TITLE", nullable = false, length = 35)
private String jobTitle;
@Column(name="MAX_SALARY")
private Long maxSalary;
@Column(name="MIN_SALARY")
private Long minSalary;
//getters and setters...
}
B. EJB Session Beans
HRFacadeBean.java
//package, imports, and entity annotation statements
public class HRFacadeBean implements HRFacade, HRFacadeLocal {
@PersistenceContext(unitName="uiShellModel")
private EntityManager em;
public Job persistJob(Job job) {
em.persist(job);
return job;
}
public Job mergeJob(Job job) {
return em.merge(job);
}
public Job findJobById(String jobId) {
if (STRING_EMPTY_OBJECT_ID.equals(jobId)){
return new Job();
}
return em.find(Job.class, jobId);
}
/** <code>select o from Job o</code> */
public List findAllJobs() {
return em.createNamedQuery("findAllJobs").getResultList();
}
}
The findJobById method above returns an empty Job object if the jobId input parameter matches a predetermined constant with the value "EMPTY"
C. ADF Task Flows
job-list-task-flow - This task flow constitutes the Overview tab. The task flow contains a single activity, the job_list.jsff which displays a table showing all the jobs.job-details-task-flow - This task flow constitutes the Jobs tab and is used to support record viewing, editing, and creation. Of importance is the specification of the data control scope for this task flow as isolated. This allows multiple instances of the jobs task flow to open as separate tabs and operate independently.The task flow takes two parameters. The first is jobId which nominates which task flow to display; and alternatively if null, to create a new job. The second parameter, editMode, indicates for an existing job whether to set a read-only view mode or allow the user to edit the job specified.
Figure 9: The "job-details-task-flow" is set as isolated by unchecking the “Share data controls with calling task flow” checkbox.
The "job-details-task-flow" is set as isolated to prevent concurrency issues especially in new record creation. It takes resources, but on the other hand it prevents re-querying of data from the service layer every time the user navigates between tabs of the same task-flow definition.
This task flow has the following activities:
isNewJob, checks if the task flow parameter jobId is null. If null, this flags the creation of a new record, or alternatively, a not null jobId displays that job.Figure 10: The "job-details-task-flow" contains a router to check if the instance is for record creation or just for viewing of an existing record.
initialize, function creates a new record via the EJB/JPA records.
public void initialize(){
Job job =
(Job)getBindings().getOperationBinding("findJobById").execute();
//do some initialization
job.setJobTitle("Default Job Title");
//ensures that form is displayed in edit-mode
getPageFlowScope().put(EDIT_MODE, true);
}
job_details.jsff, is used to view, edit, or create a new Job. This fragment utilizes findById so that multiple record viewing is possible.
D. ADF Pages & Fragments
job_list.jsff page fragment shows the list of jobs in a table. This is launched as a new tab from the “Jobs” navigation link:
<af:commandToolbarButton text="New" id="ctb2" actionListener="#{backingBeanScope.jobListForm.create}"/>
job_details.jsff, supports the view, edit, and create record pages.
Input components on this fragment are disabled thru the following expression in the readOnly property:
<af:inputText value="#{bindings.jobTitle.inputValue}"
label="#{bindings.jobTitle.hints.label}"
required="#{bindings.jobTitle.hints.mandatory}"
columns="#{bindings.jobTitle.hints.displayWidth}"
maximumLength="#{bindings.jobTitle.hints.precision}"
shortDesc="#{bindings.jobTitle.hints.tooltip}" id="it3"
readOnly="#{!pageFlowScope.editMode}"
autoSubmit="true">
<f:validator binding="#{bindings.jobTitle.validator}"/>
<f:valueChangeListener type="src.view.listeners.DirtyListener"/>
</af:inputText>
E. ADF Managed Beans
BaseForm.java is the super class of the backing beans. This hosts methods that can be used by the backing beans like launchActivity, getCurrentRowDataProvider, closeCurrentTab, setCurrentTabDirty, getMatchingTabIndex, and others.JobListForm.java is the backing bean of job_list.jsff. This contains action listeners that support the New and Edit buttons and the links on each record.
public class JobListForm extends BaseForm {
private static final String JOB_DETAILS_TASK_FLOW =
"/WEB-INF/flows/job-details-task-flow.xml#job-details-task-flow";
public void view(ActionEvent actionEvent) {
launchJobDetails(false);
}
public void edit(ActionEvent actionEvent) {
launchJobDetails(true);
}
public void create(ActionEvent event) {
Map<String, Object> parameterMap = new HashMap<String, Object>();
parameterMap.put("jobId", STRING_EMPTY_OBJECT_ID);
String title = "Job: *";
launchActivity(title, JOB_DETAILS_TASK_FLOW, parameterMap, true);
}
private void launchJobDetails(boolean editMode){
Job job = (Job)getCurrentRowDataProvider("findAllJobsIterator");
Map<String, Object> parameterMap = new HashMap<String, Object>();
parameterMap.put("jobId", job.getJobId());
parameterMap.put(IDENTIFIER, job.getJobId());
if (editMode){
parameterMap.put(EDIT_MODE, editMode);
}
String title = "Job: " + job.getJobId();
launchActivity(title, JOB_DETAILS_TASK_FLOW, parameterMap, true);
}
}
JobDetailsForm.java is the backing bean of job_details.jsff. This contains action listeners that support the Cancel, and Save and Close buttons. The action listener that supports the Edit button is inherited from the BaseForm.
public class JobDetailsForm extends BaseForm {
private static final String JOB_LIST_TASK_FLOW =
"/WEB-INF/flows/job-list-task-flow.xml#job-list-task-flow";
public void cancel(ActionEvent actionEvent) {
setCurrentTabClean();
closeCurrentTab();
//tries to activate the Overview tab if present
activateTab(JOB_LIST_TASK_FLOW);
}
public void saveAndClose(ActionEvent actionEvent) {
save();
setCurrentTabClean();
closeCurrentTab();
activateTab(JOB_LIST_TASK_FLOW);
}
private void save() {
OperationBinding oper = null;
if (isNewJob()) {
oper = getOperationBinding("persistJob");
} else {
oper = getOperationBinding("mergeJob");
}
oper.execute();
}
private boolean isNewJob() {
return STRING_EMPTY_OBJECT_ID.equals(getPageFlowScope().get("jobId"));
}
// initialize method
}
To recreate the demonstration used to illustrate the Create, Read and Update behavior within the UI Shell, follow the steps below. The implementation of this demonstration is divided into 7 major steps.
Create Workspace
To start the development of this demonstration application:
UIShell_EJB_CRU for the Application Name. Ensure there are no spaces in the name.src.uiShellViewController for the ViewController project.uiShell_EJB_Model for the project name. Note the EJB and Java default project features.Create Entities from Tables: JPA Entity Model Object
To define the EJB/JPA objects for our Model project:
Job without the "s".Job.java class isn't available in the uiShellViewController project and should JDeveloper search for it elsewhere, click Yes.Create a Predetermined Constant
Later on in the job-detail-task-flow.xml task flow, when creating a brand new Job record, pre-populate the Job record with the defined constant EMPTY for the JobId. This will allow the job-detail-task-flow to know if it's working with a new record or an existing record. This will change the operations the user can undertake on the record.
To create a predetermined constant:
uiShell_EJB_Model project of the Application Navigator and select New.src.constants.src.constants package in the Application Navigator and select New.Constants for the Name in the Create Java Class dialog.
public static final String STRING_EMPTY_OBJECT_ID = "EMPTY";
public static final Long LONG_EMPTY_OBJECT_ID = -1L;
Create EJB Session Bean
In order to allow the application to access the Job.java class that was created by the Entities from Tables wizard, we must provide an EJB session bean:
uiShell_EJB_Model project in the Application Navigator and select New.HRFacade. Tick the box that says Generate Session Façade Methods and select the uiShell_EJB_Model as the persistence unit.src.service.HRFacadeBean.Modify HRFacadeBean
The previous wizard creates the HRFacadeBean.java session bean and supporting artifacts. For the purposes of this application modifications are made to it.
In the resulting HRFacadeBean.java class:
import static src.constants.Constants.STRING_EMPTY_OBJECT_ID;
findJobById method to HRFacadeBean to return an empty Job object if the id argument that was passed into the method corresponds to a predetermined constant with value like EMPTY for String IDs. This will assist the job-details-task-flow that we create later on to distinguish between new Job records and existing Job records:
public Job findJobById(String jobId) {
if (jobId == null){
throw new IllegalArgumentException();
}
if (STRING_EMPTY_OBJECT_ID.equals(jobId)){
return new Job();
}
return em.find(Job.class, jobId);
}
getJobFindAll method in HRFacadeBean with the new method findAllJobs:
/** select o from Job o */
public List<Job> findAllJobs() {
return em.createNamedQuery("findAllJobs").getResultList();
}
Modify HRFacade
In addition to the modifications made to HRFacadeBean, modifications to the the two interfaces that it is based on are needed.
Within the Application Navigator in the uiShell_EJB_Model project locate the HRFacade.java class under the src.service package:
HRFacade.java:
Job findJobById(String jobId);
getJobFindAll method with the following in HRFacade.java:
List<Job> findAllJobs();
Modify HRFacadeLocal
Now locate the HRFacadeLocal.java interface and do the same:
Job findJobById(String jobId);
getJobFindAll with the following:
List<Job> findAllJobs();
Modify Job
The getJobFindAll method with findAllJobs leaves a dangling reference in the Job.java class. Locate the Job.java class and the @NamedQueries annotation at top.
Expand @annotations:
@NamedQueries( { @NamedQuery(name = "findAllJobs", query = "select o from Job o") })
@Table(name = “JOBS") to @Table(name = “HR.JOBS")Create EJB Data Control
Now that we've created the basic Model layer for our application we need to expose this to the ViewController to access.
This is done through the provisioning of ADF Data Controls that make the data from the Model project available to the user interface in an ADF Fusion Web Application:
context menu on HRFacadeBean.java in the src.service package of the uiShell_EJB_Model project and select Create Data Control.Create Page Based on ADF UI Shell Template
We now have our basic Model business service for our application. From here we'll create the pages, page fragments and task flows of our application.
The first thing to create is our main landing page based on the ADF UI Shell. The best way to summarize the ADF UI Shell is it's a template with behaviours. The most salient behavior supported by the UI Shell is dynamic tabs. The tabs are dynamic in that they are rendered and dismissed upon demand by the user.
The task flows created later will display as regions within dynamic tabs within a page based on the ADF UI Shell template.
uiShellViewController project.JSF / Facelets node, followed by the Page in the items list.First without any file type (e.g. .jspx or .jsf)pagespages subdirectory selected
View menu of the IDE. af:document node. You may have to expand f:view to see it.Navigation Pane and Dynamic Tabs.Add a Global Tab to the Page
Although this demonstration application has only one page, the ADF UI Shell supports multiple dynamic tabs. This allows multiple pages to present themselves as tab choices to the user. Once the ADF UI Shell page is created we'll later use its associated APIs to call backing beans to manage our tabs.
Steps to create a global tab :
uiShellViewController project locate and open the First.jspx in the Application Navigator.Navigation Pane and Dynamic Tabs for the Text property.true.As stated creating a page based on the ADF UI Shell Template was necessary to expose its APIs that are used by the backing beans created in the next section. The UI for this page is completed after the creation of the following backing beans.
Create Backing Beans
We'll now create four backing beans to serve as action listeners supporting actions like New, Edit, and View details, as well as accessing ADF UI Shell APIs for adding or selecting dynamic tabs and indicating to the user whether a task flow is clean or dirty. The code contained within these beans is mostly unexplained and will be left for you to investigate at your leisure.
Create Class BaseForm.java
Steps to create is a super class, BaseForm.java, containing methods that access the ADF UI Shell Template APIs inherited by other classes:
uiShellViewController project.BaseForm.src.view.backing.
package src.view.backing;
import java.util.List;
import java.util.Map;
import javax.faces.component.UIComponent;
import javax.faces.event.ActionEvent;
import oracle.adf.model.BindingContext;
import oracle.adf.model.bean.DCDataRow;
import oracle.adf.model.binding.DCIteratorBinding;
import oracle.adf.view.rich.context.AdfFacesContext;
import oracle.binding.BindingContainer;
import oracle.binding.OperationBinding;
import oracle.jbo.RowSetIterator;
import oracle.ui.pattern.dynamicShell.Tab;
import oracle.ui.pattern.dynamicShell.TabContext;
public class BaseForm {
public static final String EDIT_MODE = "editMode";
public static final String IDENTIFIER = "identifier";
protected void launchActivity(String title, String taskflowId,
Map <String, Object> parameterMap,
boolean newTab) {
try {
TabContext tabContext = getTabContext();
if (newTab) { //allows multiple instance of taskflow.
//if parameters contains identifier
//try to select the taskflow with matching identifier
Object identifier = parameterMap.get(IDENTIFIER);
if (identifier != null) {
int index = getMatchingTabIndex(taskflowId, identifier);
if (index != -1) {
activateTab(index);
return;
}
}
tabContext.addTab(title, taskflowId, parameterMap);
} else {
tabContext.addOrSelectTab(title, taskflowId, parameterMap);
}
} catch (TabContext.TabOverflowException toe) {
// causes a dialog to be displayed to the user saying that there are
// too many tabs open - the new tab will not be opened...
toe.handleDefault();
}
}
public void launchMenu(ActionEvent event) {
UIComponent component = event.getComponent();
String title = (String)component.getAttributes().get("title");
String taskFlowId =
(String)component.getAttributes().get("taskflowId");
Map<String, Object> parameterMap =
(Map<String, Object>)component.getAttributes().get("parameterMap");
Boolean newTab = (Boolean)component.getAttributes().get("newTab");
if (newTab == null) {
newTab = false;
}
launchActivity(title, taskFlowId, parameterMap, newTab);
}
public Object getCurrentRowDataProvider(String iteratorName) {
BindingContainer bindings = getBindings();
DCIteratorBinding dcib = (DCIteratorBinding)bindings.get(iteratorName);
RowSetIterator iter = dcib.getRowSetIterator();
DCDataRow row = (DCDataRow)iter.getCurrentRow();
return row.getDataProvider();
}
public BindingContainer getBindings() {
return BindingContext.getCurrent().getCurrentBindingsEntry();
}
public void setCurrentTabDirty(boolean dirty) {
getTabContext().markCurrentTabDirty(dirty);
}
public void setCurrentTabClean() {
setCurrentTabDirty(false);
}
public void setCurrentTabDirty() {
setCurrentTabDirty(true);
}
public Map<String, Object> getPageFlowScope() {
return AdfFacesContext.getCurrentInstance().getPageFlowScope();
}
public void edit(ActionEvent event) {
getPageFlowScope().put(EDIT_MODE, true);
}
protected OperationBinding getOperationBinding(String methodAction) {
OperationBinding oper =
getBindings().getOperationBinding(methodAction);
if (oper == null) {
throw new IllegalArgumentException(methodAction +
" operation not found");
}
return oper;
}
public void closeCurrentTab(ActionEvent actionEvent) {
closeCurrentTab();
}
public void closeCurrentTab() {
getTabContext().removeCurrentTab();
}
public TabContext getTabContext() {
return TabContext.getCurrentInstance();
}
protected void updateCurrentTabTitle(String title) {
getCurrentTab().setTitle(title);
}
protected Tab getCurrentTab() {
TabContext tabContext = getTabContext();
return tabContext.getTabs().get(tabContext.getSelectedTabIndex());
}
public boolean isCurrentTabDirty() {
return getTabContext().isCurrentTabDirty();
}
protected int getMatchingTabIndex(String taskflowId, Object identifier) {
if (taskflowId == null || identifier == null) {
return -1;
}
List<Tab> tabs = getTabContext().getTabs();
for (int i = 0; i < tabs.size(); i++) {
Tab tab = tabs.get(i);
if (tab == null || !tab.isActive())
continue;
if (tab.getTaskflowId().getFullyQualifiedName().equals(taskflowId)) {
Map<String, Object> parametersMap = tab.getParameters();
if (identifier.equals(parametersMap.get(IDENTIFIER))) {
return i;
}
}
}
return -1;
}
protected void activateTab(int index) {
if (index == -1) {
return;
}
getTabContext().setSelectedTabIndex(index);
}
//activates a tab with a specified taskflowID
protected void activateTab(String taskflowId) {
activateTab(getTabContext().getFirstTabIndex(taskflowId));
}
}
Ensure ViewController project has recognized dependency on the Model project
The uiShellViewController project has an obvious dependency on the Model project. However, it is not recognized by default.
To rectify this we will set project properties to map the dependency:
uiShellViewController project.Create Class JobListForm.java
The second class to create JobListForm.java will be the backing bean of the page fragment job_list.jsff created later. It inherits, among other things, a dynamic tab launch method from BaseForm.java while providing action listeners that support the New button and a view link.
uiShellViewController project.JobListForm.src.view.backing.
package src.view.backing;
import java.util.HashMap;
import java.util.Map;
import javax.faces.event.ActionEvent;
import static src.constants.Constants.STRING_EMPTY_OBJECT_ID;
import src.Job; // If this doesn’t work try src.model.Job instead
public class JobListForm extends BaseForm {
private static final String JOB_DETAILS_TASK_FLOW = "/WEB-INF/Taskflows/job-details-task-flow.xml#job-details-task-flow";
public void view(ActionEvent actionEvent) {
launchJobDetails(false);
}
public void edit(ActionEvent actionEvent) {
launchJobDetails(true);
}
public void create(ActionEvent event) {
Map<String, Object> parameterMap = new HashMap<String, Object>();
parameterMap.put("jobId", STRING_EMPTY_OBJECT_ID);
String title = "Job: *";
launchActivity(title, JOB_DETAILS_TASK_FLOW, parameterMap, true);
}
private void launchJobDetails(boolean editMode){
Job job = (Job)getCurrentRowDataProvider("findAllJobsIterator");
Map<String, Object> parameterMap = new HashMap<String, Object>();
parameterMap.put("jobId", job.getJobId());
parameterMap.put(IDENTIFIER, job.getJobId());
if (editMode){
parameterMap.put(EDIT_MODE, editMode);
}
String title = "Job: " + job.getJobId();
launchActivity(title, JOB_DETAILS_TASK_FLOW, parameterMap, true);
}
}
Create Class JobDetailsForm.java
The third class to create JobDetailsForm.java is the backing bean of page fragment, job_details.jsff, which we will create later.
This contains action listeners that support the Cancel, Save and Close buttons. The action listener that supports the Edit button is inherited from the BaseForm.
uiShellViewController project.JobDetailsForm.src.view.backing.
package src.view.backing;
import javax.faces.event.ActionEvent;
import oracle.binding.OperationBinding;
import static src.constants.Constants.STRING_EMPTY_OBJECT_ID;
import src.Job; // If this doesn’t work try src.model.Job instead
public class JobDetailsForm extends BaseForm {
private static final String JOB_LIST_TASK_FLOW =
"/WEB-INF/Taskflows/job-list-task-flow.xml#job-list-task-flow";
public void cancel(ActionEvent actionEvent) {
setCurrentTabClean();
closeCurrentTab();
//tries to activate the Overview tab if present
activateTab(JOB_LIST_TASK_FLOW);
}
public void saveAndClose(ActionEvent actionEvent) {
save();
setCurrentTabClean();
closeCurrentTab();
activateTab(JOB_LIST_TASK_FLOW);
}
private void save() {
OperationBinding oper = null;
if (isNewJob()) {
oper = getOperationBinding("persistJob");
} else {
oper = getOperationBinding("mergeJob");
}
oper.execute();
}
private boolean isNewJob() {
return STRING_EMPTY_OBJECT_ID.equals(getPageFlowScope().get("jobId"));
}
public void initialize() {
Job job =
(Job)getBindings().getOperationBinding("findJobById").execute();
//do some initialization
job.setJobTitle("Default Job Title");
//ensures that form is displayed in edit-mode
getPageFlowScope().put(EDIT_MODE, true);
}
}
Create Class DirtyListener.java
The fourth class to create, DirtyListener.java, is the backing bean of page fragment job_details.jsff. It is a custom class that implements the ValueChangeListener interface and calls the markCurrentTabDirty ADF UI Shell API. When the value of a UI component associated with the DirtyListener class changes, the text title of the dynamic tab of the ADF UI Shell will turn to italics. This is a signal to the user that the current tab now has dirty/modified data.
This is useful feedback when there are potentially several open tabs in the UI.
uiShellViewController project.DirtyListener.src.view.listeners.
package src.view.listeners;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ValueChangeEvent;
import javax.faces.event.ValueChangeListener;
import oracle.ui.pattern.dynamicShell.TabContext;
public class DirtyListener implements ValueChangeListener {
public void processValueChange(ValueChangeEvent valueChangeEvent) throws AbortProcessingException {
TabContext tabContext = TabContext.getCurrentInstance();
if (tabContext != null) {
tabContext.markCurrentTabDirty(true);
}
}
}
Create ADF Taskflows
The demonstration application is composed of two task flows; One which is launched for the overview tab and the other, which supports the viewing, editing, and creation of records in additional tabs. These task flows are called via an API provided by the ADF UI Shell Template invoked via our beans.
Create Taskflow: Job List
job-list-task-flow for the task flow name.Taskflows. Note the case of this directory is important, and ensure you add no spaces.Register JobListForm Bean
UI components are components of page or page fragments which are accessed via view activities in task flows. Such UI components make use of the backing beans that we created earlier to provide values and behavior. To bind UI components to the created backing beans we must first register that bean to the owning task flow.
Thereafter we can reference the backing bean as a property value of a UI component through an EL expression:
job-list-task-flow, select Overview.jobListForm for the Name. src.view.backing.JobListForm for the Class.tabContext for the Name.Design Page Fragment: Overview Tab
job_list.fragments.Manage Jobs.Jobs.detach for the FeaturesOff value.
#{row.jobId}#{backingBeanScope.jobListForm.view}af:outputText. Select and delete this.New.Edit.Refresh.default by selecting the option from the right most edit button drop down icon.Create Taskflow: Job Details
job-details-task-flow for the task flow name
Register JobDetailsForm Bean
job-details-task-flow, select Overview.jobDetailsForm for the Name.tabContext for the Name.jobId for the Name. Note the case of the name is important.editMode for the Name.Diagram of JobDetailsForm Taskflow
job-details-task-flow, select Diagram.isNewJob for the name.initialize for the name.job_details for the name.initialize for the name of the Control Flow Case.toEdit for the name of this Control Flow Case.toEdit for the name.#{pageFlowScope.jobId eq 'EMPTY'}for Expression. #{backingBeanScope.jobDetailsForm.initialize} for the Method property.toEdit in the Fixed Outcome drop down.#{pageFlowScope.jobId} Click Ok.Design Page Fragment: Job Detail Tab
#{pageFlowScope.editMode? 'Edit ':'View '}Job: #{bindings.jobId}Basic Information.#{bindings.jobId.inputValue}. #{!pageFlowScope.editMode or bindings.findJobByIdIterator.currentRow.dataProvider.jobId ne null}bindings.findJobByIdIterator.currentRow.dataProvider.jobId line may be underlined with red. Ignore this for now.#{!pageFlowScope.editMode} src.view.listeners.DirtyListener#{not pageFlowScope.editMode}#{backingBeanScope.jobDetailsForm.edit} in the actionListener property.#{not pageFlowScope.editMode}#{backingBeanScope.jobDetailsForm.cancel} in the actionListener property.#{pageFlowScope.editMode}#{backingBeanScope.jobDetailsForm.saveAndClose} in the actionListener property.#{pageFlowScope.editMode}#{backingBeanScope.jobDetailsForm.cancel} for the actionListener.jobs_details.jsff page fragment still open select the Bindings tab at the bottom of the editor. #{bindings.findJobByIdIterator.currentRow.dataProvider}#{bindings.findJobByIdIterator.currentRow.dataProvider}Completing the First page
At this point of time we have all the artefacts in place for our application. To complete it we simply need to add the ability to call the Overview tab (job-list-task-flow) from the First.jspx page:
First.jspx page via the Application Navigator.Navigation.Menu.Jobs.#{backingBeanScope.launcher.launchMenu} title and the value property to Overview.taskflowId and value property to /WEB-INF/Taskflows/job-list-task-flow.xml#job-list-task-flow.You'll note in the previous steps a call to a managed bean named "launcher". We must configure this in the application's adfc-config.xml file.
adfc-config.xml file and open it.launcher for the Name.First.jspx page, locate the Document component and its Meta Container facet. If it shows a greyed-out folder, then right click it and select Facets – Document then Meta Container to create itFirst.jspx page editor select the Source tab at bottom of the editor page.
Replace the Meta Container facet code as follows:
<f:facet name="metaContainer">
<af:group id="g1">
<af:resource type="javascript">
function setCurrentTabDirty(event) {
var source = AdfPage.PAGE.findComponent("d1");
AdfCustomEvent.queue(source, "setCurrentTabDirty", {},false);
}
</af:resource>
</af:group>
</f:facet>
Instead of completing these step, an ADF application on OTN called ADF_UIShell_EJB_CRU is available at [LINK]. It can be unzipped into the designated work area and opened with JDeveloper. This pattern has been tested against JDeveloper Studio 11.1.1.6.0.
Related Documentation
| Documentation Related to the Pattern Artifacts | |
|---|---|
| Oracle Dynamic Tabs Template | Dynamic Tabs UI Shell Template Functional UI Pattern in Oracle ADF Functional Patterns |
| Enable/Disable an ADF Component | Enable/Disable an ADF Component Functional UI Pattern in Oracle ADF Functional Patterns |
| ADF Task Flows | Working with Task Flow Activities in Oracle ADF Functional Patterns |
| Contextual Events | Creating Contextual Events in Fusion Developer’s Guide |
| ADF Model (JSR-227) | Using ADF Model Data Binding in a Java EE Web Application in the Java EE Developer's Guide |
| EJB/JPA | Getting Started with an EJB Business Services Layer in JDeveloper Online Help |