Introducing Page Templates
Page templates allow us to specify the structure of the pages in our application through a single
entity: the page template. In this template we can specify the layout structure - top bar, footer, side
bar, logo’s, copyright message etc. Pages can be created based on the page template. The page
specifies the content that is pasted in the designated areas in the template.
When we want to change the overall look & feel of our application, we only need to make those
changes in the page template, and all pages automatically display with those changes.
Objective We will create a new Page Template with two facetRefs and a parameter. The pages based on the
template have to provide the content for the two facets and the parameter. We will create two
pages based on the template. We run the application. Then we decide to make a change in the
template. And re-run the pages to see the changes applied.
Steps
1. Create a new JDeveloper Application IntroductionPageTemplate - Generic Application;
Choose the ADF Faces library.
2. Go to the New Gallery. Click on the JSF Category and select the JSF Page Template item.
Create JSF Page Template. Call it mainDataTemplate.jspx
Specify two facets: body and aboutWindow.
Go to the attributes page. Define three attributes: pageTitle, showGlobalSearch and
selectedMenuItem.
Now we will create the look and feel of our application. Or at least of the main template used in the
application. However, it does not need to look good at this point. As long as the right number of
facets is defined as well as the required attributes, we can leave the pageTemplate as ugly as we like.
This is because we can change it any time, without having to modify any of the pages. So although
our developers would probably prefer to have a decent template early on, there is no real need for it.
3. Create the main layout in the pageTemplate. Here are some steps that may give you a
reasonable idea of what you could do in a template (it does not really matter what you do, as
long as you use the two facets and ideally the three attributes as well)
Drag a PanelSplitter to the template page; drop it inside the af:pageTemplateDef
element (in the Structure Window); set the orientation attribute to Vertical
Drag a panelGroupLayout to the first panelSplitter facet. Drag a panelBorderLayout inside
the panelGroupLayout.
The content of the panelBorderLayout could be like this:
<af:panelBorderLayout>
<f:facet name="bottom">
<af:outputLabel value="#{attrs.pageTitle}"
inlineStyle="font-family:Verdana, Arial,
Helvetica, sans-serif; font-size:xx-large; color:Blue; font-weight:bolder;"/>
</f:facet>
<f:facet name="innerLeft">
<af:image source="/img_amis_logo.gif"/>
</f:facet>
<f:facet name="right">
<af:image source="/helpIcon.jpg" inlineStyle="width:35px;">
<af:showPopupBehavior triggerType="click"
popupId="aboutWindowPopup"/>
</af:image>
</f:facet>
</af:panelBorderLayout>
Here we specify two images and an outputText with some styling. This outputText
renders the title of the page and uses the value of the pageTitle attribute configured for
the pageTemplate. The second image is a question mark icon. Associated with this image
is a showPopupBehavior that specifies that when the image is clicked upon, the
aboutWindowPopup Popup should be displayed. We will create the Popup in a moment.
Drag another panelSplitter inside the second facet of our vertical Panel Splitter. This
splitter has a horizontal orientation. Set a background color on this splitter, for example
the light blue of the screenshot.
Drag a vertical panelGroupLayout to the first facet of this new splitter. Drag a facetRef to
the second splitter facet and drop it. Choose the body Facet from dropdown list, as that
is where the contents passed into the template through the body facet is to go.
The contents of the first facet of the horizontal panelSplitter could be something like this:
<af:panelGroupLayout layout="vertical">
<af:panelBorderLayout inlineStyle="height:100%;">
<f:facet name="bottom">
<af:outputText value="This application supports the
improvement of every day life for operators."
inlineStyle="background-
color:rgb(214,231,255); vertical-align:bottom;"/>
</f:facet>
<f:facet name="top">
<af:panelList maxColumns="1">
<af:goLink text="Main Menu"
inlineStyle="#{attrs.selectedMenuItem=='main'?'font-weight:bolder; font-
size:large;':''};"/>
<af:goLink text="Adminstration Menu"
inlineStyle="#{attrs.selectedMenuItem=='admin'?'font-weight:bolder; font-
size:large;':''};"/>
<af:goLink text="Your Worklist"
inlineStyle="#{attrs.selectedMenuItem=='work'?'font-weight:bolder; font-
size:large;':''};"/>
</af:panelList>
</f:facet>
<f:facet name="innerBottom">
<af:spacer width="10" height="50"/>
</f:facet>
</af:panelBorderLayout>
</af:panelGroupLayout>
The most interesting bit is the panelList with three imaginary menu options. The styling of
these menu options depends on the value of the selectedMenuItem attribute that is
specified by the pageTemplate and whose value is to be passed to the template from each
based based on it. The idea is that the page indicates which menu option should be
highlighted.
Let’s now create the Popup that is to be shown when the Help (Question Mark) image is
clicked:
Drag a Popup and drop it just under the PanelSplitter node; set its id attribute to
aboutWindowPopup.
Drag a panelWindow inside the Popup. Specify a title like About this Page.
Drag a facetRef component inside the panelWindow; choose the aboutPage facet in the
drop down list.
Time to create a page that is based on this template.
4. Create a new JSF page; call it MyFirstPage.,jspx. Choose the mainDataTemplate as the
template to base the page on.
5. Add f:attribute children to the af:pageTemplate element to pass values for the template’s
parameters:
<af:pageTemplate viewId="/mainDataTemplate.jspx">
<f:attribute name="pageTitle"
value="The title of the first page in this application" />
<f:attribute name="selectedMenuItem"
value="admin" />
…
6. Create content for the two facets that the template has predefined for this - and every other
page based on it.
Drag a rich text editor to the body facet.
Drag an image to the aboutPage facet.
Add a little bit help for our friends:
<f:facet name="aboutPage">
<af:panelHeader text="Explaining The Rich Text Editor
Page"
inlineStyle="width:450px;">
<af:image source="/img_amis_logo.gif"/>
<af:outputText value="This page is used for editing
textual content. It allows you to not only specify the text itself but
some of its mark up too. You can use it for specifying simple mark up
like Bold, Underline and Italic. It also allows setting of fonts, font
size, coloring and both bullet and number lists. Indentation and left
vs. right aligning is among the functionality too."
/>
</af:panelHeader>
</f:facet>
7. Run the page:
Notice how the page for which we only defined a title, a selectedMenuItem, the content of
the about window and the content for the body displays with a lot of extras. There is a
structure to the page, with a header with a logo and a sidebar with a menu. Of course that is
all added by the template that is applied at runtime.
Click on the big question mark. Now the popup behavior kicks in and opens the aboutPage
popup, that displays the content that we injected through the about facet - the little help for
our friends.
8. Let’s quickly create a second page, now that we get the hang of it.
Create a new JSF Page, select the same mainDataTemplate template. Provide values for the
attributes:
To quickly throw something together, I have dragged a panelGrid (JSF HTML library) to the
body facet and included image links into it (just random images from a Google Image search).
I provided content for the about facet with a PanelHeader and some OutputFormat text.
9. Run this page too.
10. Although the overall look & feel is consistent between “all” pages, it is consistently lousy. So
we need some changes.
Go to the page template and make for example the following changes:
Choose another Font and Style for the Page Title.
Use a different logo in the header and/or show the logo in the footer of the side bar.
Add a footer with a copyright message
Change the background color
Add one or two menu options
Add a global search item in the side bar - that is only shown when the
showGlobalSearch attribute has the value true
11. Now run the pages again - see how by changing a single template we impacted our entire
application! (the value of that statement increases with the number of pages in your
application, obviously)
Bonus Practice: Dynamic, Context Sensitive Template Selection Extra: the template reference in the pages can be specified through an EL expression. That means the
application can decide at run-time which template to apply - as long as the number of facets defined
for the templates is equal. The template - and therefore the look & feel/layout structure - can thus
be made context sensitive. See for a more detailed description this blog article:
http://technology.amis.nl/blog/3702/keeping-up-appearances-with-adf-11g-richfaces-context-
sensitive-styling-in-a-world-of-imperfect-html-with-dynamic-pagetemplate-and-page-fragment-
includes
1. Now try this out:
create a class that has a property currentTemplate
configure a managed bean for that class
change the template reference in the page to an EL expression referencing that bean and
property
create a new page template; include the same facets (body and about) and the same
attributes; create the layout structure for this new template
create a radio group or dropdown item in both templates; the select items refer to the
two templates; the value attribute of the select control is associated with the same EL
expression as used in the template reference, namely the bean and currentTemplate
property.
Create a Page Template
Embed one or more facetRef elements for the holes to be filled by the pages based
on the template
Publish the template “API” – the input parameters it supports
Create pages based on the template
In these pages, provide content for the facets exposed by the template
Provide values for input parameters supported by template
At any point in time:
change the template (reusing same facetRefs and parameters)
change the facet contents in any page
change template ref in a page (to template with same facets)
• Can be done dynamically
The steps for creating this functionality
1. Create Fusion Web Application
2. In Model Project, create default Dept and Emp Entity and View Objects, with ViewLink
between Dept and Emp. Ensure to set Deptno and Empno to be Primary Key attributes.
3. Create a new JSF page - DeptEmp.jspx.
Drag EmpView from Data Control palette and drop as Master Form Detail Table. Only include
Empno and Ename in the Emp table.
4. Create a class EmpTableManager. Configure a managed bean EmpTableManager based on
this class.
<managed-bean>
<managed-bean-name>EmpTableManager</managed-bean-name>
<managed-bean-class>view.EmpTableManager</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
Add private member employeeKeyString and method processSelectionEvent() in the class:
String employeeKeyString;
public void processSelectionEvent(SelectionEvent selectionEvent) {
RowKeySet rks = selectionEvent.getAddedSet();
List k = (List)rks.iterator().next();
Key empKey = (Key)k.get(0);
employeeKeyString = empKey.toStringFormat(false);
}
5. Set the selectionListener attribute on the Employee table. Make sure the
EmpTableManager’s processSelectionEvent is called whenever a row is selected in the table:
<af:table …
selectionListener="#{EmpTableManager.processSelectionEvent}"
6. You can run the page and see a perfectly ordinary Master-Detail page with Form-Table
layout, navigation buttons for the Form to browse through departments
Now things start getting more interesting: we are about to first create and then embed a bounded
task flow.
7. Create New Bounded Task Flow. Go to the New Gallery (main menu File, New)
Name the task flow employee-form. Ensure that the checkbox Create as Bounded Task Flow
is checked (that is the default so it should be).
8. Create Method Binding for AllEmloyeesView setCurrentRowWithKey action: drag the
SetCurrentRowWithKey action from the Data Controls Pallet and drop it on the task flow
diagram.
This creates the SetCurrentRowWithKey action binding.
Right click the SetCurrentRowWithKey method call and select the option Edit Binding. In the
Action Binding editor specify the EL expression for the rowKey parameter:
#{pageFlowScope.empKey}. We will define an inputParameter on the Task Flow that stores
its value in that location.
9. Also create View EmployeeForm, by dragging a View activity from the Component Palette to
the task flow diagram.
10. Create a navigation flow between the two, using the default outcome of the MethodCall -
setCurrentRowWithKey - as we will always go to the EmployeeForm View after setting the
current row.
The diagram should now look like this:
11. Configure an input parameter on the task flow: select the task-flow in the Application
Navigator. In the Structure Window, select the task-flow-definition root node. In the
property inspector, you can now edit the parameters.
Add a new inputParameter by clicking on the green Plus icon, after disclosing the Parameters
panel.
It is called employeeKey to the outside world. The value passed for that parameter is stored
in #{pageFlowScope.empKey} where it can be used in the SetCurrentRowWithKey call.
12. Time to create the View: the Employee Form.
Double click on the EmployeeForm view activity in the Task Flow. This will take you to the JSF
Visual Page Editor.
Drag the Employees collection to the page and drop it as an ADF Form. Include a Submit
Button.
This concludes the construction of the Bounded Task Flow - at least for now. We will return to the
DeptEmp page to embed the Task Flow in order to add additional functionality using this reusable
component.
13. Go to the DeptEmp.jspx page.
Drag the employee-form task flow from the Application Navigator and drop it on the page,
next to the Employees Table.
Set the value for the employeeKey input parameter of the task flow to
#{EmpTableManager.employeeKeyString}.
The Task Flow Binding - the usage by the page of the Task Flow - is added to the Page
Definition of the page, that also has all data binding definitions. You can edit the Task Flow
Binding, for example to change the values passed into input variables. Or to change the
Refresh settings for the Task Flow.
14. The Task Flow is refreshed at least once, when the page it is embedded in is first displayed.
We want it to also refresh when another employee row is selected in the table. We have
taken care that when a row is selected, the bean property that provides the value for the
Task Flow’s employeeKey input parameter is properly updated - through the
selectionListener in the EmpTableManager bean.
However, we need to do one other small thing to make the region refresh whenever the
value of its input parameter refreshed, and that is to set the Refresh property on the Task
Flow Binding to ifNeeded.
15. We can now run the page. Whenever we click on another employee row in the
EmployeesInDepartment table, the employee detail form on the right side is synchronized.
Bonus Practice: Synchronize when browsing Departments When we switch departments, the Employee Form is not automatically synchronized. It only
refreshes when the #{EmpTableManager.employeeKeyString} changes, which currently only happens
when a row is selected in the Employees table. So in order to have the Employee Form synchronized
with a new Department, we have to ensure that we set the employeeKeyString to correct employee
key when a new Department becomes the current one - that is when either of the four navigation
buttons is activated.
We can capture the First/Last/Previous/Next actions and invoke the EmpTableManager and have it
determine the current Employee in the EmployeesInDepartmentIterator then set the key for that
Employee in the employeeKeyString property.
1. Add this action attribute to each of the four navigation buttons:
action="#{EmpTableManager.synchronizeWithCurrentEmployee}"
2. Add the synchronizeWithCurrentEmployee() method to the EmpTableManager class:
public String synchronizeWithCurrentEmployee() {
Object bc = BindingContext.getCurrent().getCurrentBindingsEntry();
DCIteratorBinding ib =
((JUFormBinding)bc).findIteratorBinding("EmployeesInDepartmentIterator");
this.employeeKeyString = ib.getCurrentRowKeyString();
return null;
}
3. Now run the page and see that whenever you navigate to a different Department, the region
with the embedded employee-form task flow is synchronized.
Bonus Practice: Split the Employee Form into a two-page overview One of the interesting features of the Bounded Task Flow infrastructure is that it allows the creation
of a multi-step, multi-page task flow that can be embedded in its entirety in another page. That
means that we can navigate between the steps in the task flow within the context of the host page.
We will see that in action.
1. Open the employee-form task-flow.
Add a new View activity. Call it EmployeeFormPartTwo.
Create Control Flow Case from EmployeeForm to EmployeeFormPartTwo; set the from-
outcome to parttwo. Create Control Flow Case from EmployeeFormPartTwo to
EmployeeForm; set the from-outcome to partone.
The task flow diagram should now look like this:
2. Double click on the new View activity. Accept the dialog that appears and go to the editor for
the EmployeeFormPartTwo.jsff page fragment.
Copy the entire contents from EmployeeForm.jsff and paste it in EmployeeFormPartTwo.jsff.
3. Go to the file DataBindings.cpx and copy the line for EmployeeForm.jsff in the pageMap
element. Change EmployeeForm.jsff to EmployeeFormPartTwo.jsff in the new line:
<pageMap>
<page path="/DeptEmp.jspx" usageId="view_DeptEmpPageDef"/>
<page path="/WEB-INF/employee-form.xml#employee-
form@SetCurrentRowWithKey"
usageId="view_employee_form_employee_form_SetCurrentRowWithKeyPageDef"/>
<page path="/EmployeeForm.jsff" usageId="view_EmployeeFormPageDef"/>
<page path="/EmployeeFormPartTwo.jsff"
usageId="view_EmployeeFormPageDef"/>
</pageMap>
This associates the Page Definition created for EmployeeForm.jsff also with
EmployeeFormPartTwo.jsff.
4. Now remove the form fields for Hiredate, Sal, Comm and Deptno from EmployeeForm.jsff
and remove the fields Empno, Ename, Job and Mgr from EmployeeFormPartTwo.jsff.
5. Add a button to EmployeeForm.jsff; its label should be Next (or Part Two). Its action should
be parttwo.
<af:commandButton text="Part Two" action="parttwo"/>
Add a button to EmployeeFormPartTwo.jsff; its label should be Previuous (or Part One). Its
action should be partone.
<af:commandButton text="Part One" action="partone"/>