Tuesday, 29 January 2008

JDev: Global buttons in page templates revisited

Recently Frank Nimphius blogged on 2 strategies for implementing global buttons in page templates.

The first relies on the use of the ADF Binding Layer and declaratively creating bindings in each consuming web page's Page Definitions file. I'm a big fan of the declarative approach as the less Java code I have to write the better.

From what I can gather in Frank's solution, it requires the commandButtons actionListener to reference #{bindings.First.execute}, which relies on the consuming web page to have the First action within its Page Definitions file. However if we extend our page template to have its own Page Definitions file (either created in the Create JSF Page Template dialog, or by right clicking the existing Page Template, selecting Go To Page Definitions and accepting yes when prompted to create the file), the EL expression #{bindings} now refers to the page template Page Definitions, not the consuming page's Page Definitions.

As such I'd like to expand upon Frank's first option and propose a 3rd method to inject the global button functionality into the page templates via attributes and gets around this limitation.

As a disclaimer while this method works in my testing under JDeveloper 11g TP3, I have not tested it in a production system, and may not be viable in later versions of JDeveloper. Therefore it is essential you test this mechanism before using it.

What we want to do is rather than randomly putting commandButtons around our page, that we build a standard toolbar for the entire application, with Create Record and Delete Record buttons (or Next/Previous buttons from Frank's example). To satisfy this requirement we're going to:
  1. Create a Page Template
  2. Add two command buttons with labels Create and Delete, with a toolbar for the Template's Static Content.
  3. Add a single Facet named "content".
  4. Add two attributes that accept Create and Delete actions from the consuming web page.
Then we're going to retrofit an existing web page containing an <af:table> and bound Create and Delete buttons to use the Template. It will:
  1. Place all its content, namely the table that shows the Events within the "content" Facet of the Page Template.
  2. Define two attributes passing in the action bindings for the local page's Create and Delete actions.
Follows these steps to implement:

1. Create a new Page Template with the following characteristics:

a. Name the file sageTemplate.jsft
b. Place the file under a new directory public_html/templates
c. Call the template sageTemplate
d. Leave the Page Definitions check-box unchecked.
e. Create a Facet named "content".
f. Create 2 attributes createButton and deleteButton, of type oracle.jbo.uicli.binding.JUCtrlActionBinding, required=true

(Note: as per my discussion's on the JDev forums, I'm not yet convinced required=true does anything)

2. Once the Template is created, insert the following code into the Static Content part of the Template:

... top of page followed by Static Content ...
    </af:xmlContent>
    <af:toolbar>
      <af:commandToolbarButton
        actionListener="#{attrs.createButton.execute}"
        text="Create" id="createButtonId"/>
      <af:commandToolbarButton
        actionListener="#{attrs.deleteButton.execute}"
        text="Delete" id="deleteButtonId"/>
    </af:toolbar>
    <af:panelFormLayout
      partialTriggers="createButtonId deleteButtonId">
      <af:facetRef facetName="content"/>
    </af:panelFormLayout>
  </af:pageTemplateDef>
</jsp:root>


Note the <af:panelFormLayout> wraps the <af:facetRef>, and forces a PPR on either of the buttons being pressed.

3. Open your existing web page and add the <af:pageTemplate> tag to import the template. Place the contents of the existing page into an appropriate <f:facet> tag.

<?xml version='1.0' encoding='windows-1252'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
          version="2.0"
          xmlns:h="http://java.sun.com/jsf/html"
          xmlns:f="http://java.sun.com/jsf/core"
          xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
  <jsp:directive.page contentType="text/html;charset=windows-1252"/>
  <f:view>
    <af:document>
      <af:messages/>
      <af:form>
        <af:pageTemplate
          viewId="/templates/sageTemplate.jsft.jspx">
          <f:attribute name="createButton"
            value="#{bindings.Create}"/>
          <f:attribute name="deleteButton"
            value="#{bindings.Delete}"/>
          <f:facet name="content">
            <af:table ..... and so on ..... </table>
          </f:facet>
        </af:pageTemplate>
      </af:form>
    </af:document>
  </f:view>
</jsp:root>


4. Delete any Create or Delete commandButtons from the page, but ensure that the Page Definitions remain for the Create and Delete actions.

5. For the <af:table>, ensure row selection is turned on for single rows.

6. Within the <af:pageTemplate> include the following <f:attribute> tags:

<f:attribute name="createButton"
  value="#{bindings.Create}"/>
<f:attribute name="deleteButton"
  value="#{bindings.Delete}"/>


Note how the value EL expressions pass in the Create and Delete bindings, namely instances of oracle.jbo.uicli.binding.JUCtrlActionBinding.

7. Run the web page and test that the Page Template delete button works correctly. The create button is a little more tricky because you would probably would want the application to navigate to another screen. As such you could pass in via a parameter a navigation named action too.


One of the things I like about this solution is the consuming web page can be selective about what actions it wants to pass to the Page Template. For instance if we have 2 sets of Create/Delete buttons on our web page for a master-detail relationship, we can choose which pair of buttons to inject.

Thanks to Frank for posting his original solutions and giving me the spark of inspiration for this post.

6 comments:

Jai said...

Hi Chris,

I was trying to implement a similar use case, with a requirement to pass "named navigation action" as mentioned in your post. I am having difficulty to identify the type of parameter that I should declare in pageTemplate to accept this navigation case. I would highly appreciate your help on this.

Thanks,
Jai

Chris Muir said...

Hi Jai

To give a quick answer on the weekend while I'm not sitting at my JDev workstation, I'd be guessing a java.lang.String. Let me know if that doesn't work for you.

Regards,

CM.

Jai said...

Hi Chris,

Thanks for taking time out on weekend to answer my query.

I should have mentioned that java.lang.String was the first thing I tried but then page throws error "Method not found: {}.editAction()", where editAction is the attribute defined in page template of type java.lang.String

I am guessing that may be named action cannot be passed as String and instead should be passed as "some ref object", similar to "#{bindings.first/next}" in case of page def method actions.
I have no luck so far on either parts:
1. How the calling page should pass the navigation action to page template.
2. How page template should handle if it is passed as String from calling page.

Any help/tip would be highly appreciated.

Thanks,
Jai

Chris Muir said...

Hi Jai

Apologize for the slow response, a busy few weeks for me.

I confirm I'm seeing the same behaviour as you for java.lang.String, namely the "Method not found" error. I've tried a few hacks to see where the issues sits and it seems to be in the pageTemplate mechanism and how it resolves EL expressions with the #{attrs} expression for the action property of any command control. In the end it looks what we're attempting to do is fine as the action property should resolve any EL expression and it's a bug. I suggest lodging an SR with Oracle Support.

As a workaround, if you have a button you want to cause navigation from the template, pass the complete button into a facet within the page template instead. Not ideal I agree but a workaround until Oracle solves the bug.

Regards,

CM.

karmati said...

Hi,
has this issue been solved? I am having the similar problem.
thanks

Aral

Chris Muir said...

Hi Aral

Apologies for the slow reply, I've been on holidays.

Grant Ronald has detailed the solution in the following OTN whitepaper:

http://www.oracle.com/technology/products/jdev/tips/fnimphius/GenericMenuBar/genericMenuBar.html

....specifically look to the "Method" tab in the declarative component dialog.

Regards,

CM.