In my current ADF Faces (10.1.3) project, I have created several custom JSF components. These components combine chunks of recurring page structure content. By using custom components, I can reuse such chunks by simply embedding tags with a little configuration – attributes and facets – in my pages. It works really well. Sometimes our thoughts about a particular recurring page element change, the component changes and we deploy the jar-file with the component definitions. None of the pages that use the components has to change. The one downside to this whole scenario, at least in te beginning, is the complexity of creating custom components. It involves creating two or three classes, setting up configuration in a TLD and faces-config.xml file and writing some rather low-level code. One particular challenge never quite got resolved: how to leverage the functionality of existing ADF Faces components in my custom components.
JDeveloper 11g surprises me with a declarative way of creating new JSF components – or at least reusable page fragments that are packaged and deployed and used as normal JSF components. All I really have to do is create a JSF page, make it look the way I want my reusable component to look, using attributes and facets to allow dynamic configuration of the component, and deploy it to an ADF Library. I do not do any programming, nor do I have to create any configuration file.
To use the new declarative component in a project, I have to add the jar for the ADF Library to the WEB-INF/lib directory of the project, add it to the list of Project Libraries and specify a Taglib library based on the jar. Next the component(s) in the custom library show up in the Component Palette and can be dragged onto my pages.
As a simple and rather silly example, let’s create a custom component that we call DynamicPanelSwitcher. It renders as a PanelBox with a title and a number of details that are either rendered on tabs or in an accordion. For now, it supports up to four children.
The steps in creating the declarative component:
1. Create a new Application that will be used for creating and maintaining the Declarative Components.
2. From the new gallery, select JSF, Declarative Component.
3. In the popup window, specify the name of the new component, the name of the JSPX file that will contain its definition, specify or create the Tag Library that will contain the component and define the facets and attributes the component will support.
Note: facets are the element used to pass content – JSF fragments – to the component to render somewhere inside its structure. In our case, we build a component that will render either a panelTabbed or a panelAccordion. The Tabs or Accordion items will contain content in their showDetailItems. That content is passed to our custom component through the four facets child1 through child4.
Next define the attributes – the dynamic configuration that can be passed into our component with every usage that is made of it. In this case, we will support a title for the overall panelBox, a switcher that specifies which type of detail container we should display – for now we only support tabbed and accordion as values but perhaps in a later version of the component we will support other ways of rendering the children. Note that the standard JSF attributes binding, id and rendered do not need to be defined here – they are automatically available and handled.
4. When you close the dialog, a JSPX file is created with the name you specified. It contains the configuration for the component, the attributes and the facets. In this file, we will now add the JSF fragment that our component should render. We can largely do that in the normal way of creating JSF pages, by dragging and dropping components to the visual editor.
We can refer to the attributes we have specified using EL expressions such as #{attrs.attributeName}. For example the title of the PanelBox is set using the title attribute: text=”#{attrs.title}”
Injecting the contents of a facet at a certain point in our custom component is done using an af:facetRef element. Simply drag the Facet Ref and drop it
in the right spot in the component definition. The popup window will you let select one of the names of the facets we had defined for the component. It then inserts code like this: <af:facetRef facetName=”child1″/>.
The component now looks like this:
<af:panelBox text="#{attrs.title}" inlineStyle="width:200px"> <af:switcher facetName="#{attrs.containerTypeSwitcher}"> <f:facet name="tabbed"> <af:panelTabbed> <af:showDetailItem text="#{attrs.child1Title}" rendered="#{attrs.child1Title!=''}"> <af:facetRef facetName="child1"/> </af:showDetailItem> <af:showDetailItem text="#{attrs.child2Title}" rendered="#{attrs.child2Title!=''}"> <af:facetRef facetName="child2"/> </af:showDetailItem> <af:showDetailItem text="#{attrs.child3Title}" rendered="#{attrs.child3Title!=''}"> <af:facetRef facetName="child3"/> </af:showDetailItem> <af:showDetailItem text="#{attrs.child4Title}" rendered="#{attrs.child4Title!=''}"> <af:facetRef facetName="child4"/> </af:showDetailItem> </af:panelTabbed> </f:facet> <f:facet name="accordion"> <af:panelAccordion> <af:showDetailItem text="#{attrs.child1Title}" rendered="#{attrs.child1Title!=''}"> <af:facetRef facetName="child1"/> </af:showDetailItem> <af:showDetailItem text="#{attrs.child2Title}" rendered="#{attrs.child2Title!=''}"> <af:facetRef facetName="child2"/> </af:showDetailItem> <af:showDetailItem text="#{attrs.child3Title}" rendered="#{attrs.child3Title!=''}"> <af:facetRef facetName="child3"/> </af:showDetailItem> <af:showDetailItem text="#{attrs.child4Title}" rendered="#{attrs.child4Title!=''}"> <af:facetRef facetName="child4"/> </af:showDetailItem> </af:panelAccordion> </f:facet> </af:switcher> </af:panelBox>
Deploying the component
Time to deploy the component and start using it. From the New Gallery choose Deployment Profile.
1. Select the ADF Library deployment type. Define a name for the profile (AMISCustomJSFComponent) and press OK and again OK.
2. Now we can execute deployment to a JAR file from the Run Menu, the Deploy option, by picking the deployment profile we just defined. JDeveloper will now create a JAR file for us with the name of the Deployment Profile: AMISCustomJSFComponent.jar.
When you open the JAR file, you will notice that JDeveloper did quite a bit of work for us: creating classes, TLD file and faces-config.xml.
Using the new custom component
In order to start using the component in a JSF project, we have to add the jar file to the WEB-INF/lib directory of that particular project.
Next, go to Edit Project Properties and choose the Libraries node. Press Add JAR/Directory and select the the jar file that was created during deployment of the custom component. Press OK. You will see the jar in the list of libraries associated with the project.
Now go to the JSP Tag Lib node in the Project Properties page. Press Add, click on the User node and press New. In the file browse dialog, select the JAR file for the Custom Component (again) from the file system. Then press OK a few times.
At this point we are probably all set to start using the new component in our JSF pages. However, sometimes I had to remove the project from JDeveloper and open it up again to make the JAR file really available to the IDE.
Check whether the custom components shows up on its own tab in the Component Palette.
To use the custom component, just drag it to the page like any other component and drop it in the desired location.
After dragging and dropping the component on the page, the source editor shows all available attributes to set values for. You can also use the property inspector to set the values for the attributes. Note: all attributes can be set using EL Expressions.
Let’s start by defining two children. We set the child titles, choose a Container Type (switched or accordion) – which can be dynamically bound as we will see later on and set the title for the overall container.
Next we can define the content for our detail elements by adding content to the child facets of our custom component. We can make facets visible by selecting the facets option on the RMB menu on the custom component in the Structure Window.
Let’s run the page.
Apparently, the two showDetailItem elements without content (and without childTitle) show up despite the rendered expression I used. I will have to look into that.
Well, to demonstrate the dynamic capabilities of my component, let’s add a switch that allows me to change the appearance on the fly:
<af:selectOneRadio id="switcher" autoSubmit="true" "label="Which Display Type do you prefer?" value="#{displaySwitcher.displayType}"> <af:selectItem label="Tabbed Display" value="tabbed"/> <af:selectItem label="Accordion Display" value="accordion"/> </af:selectOneRadio>
I also created a class DisplaySwitcher and configured it as a managed bean in faces-config.xml.
Finally, we change the ContainerTypeSwitcher attribute in the DynamicPanelSwitcher component to the following EL Expression: #{displaySwitcher.displayType}. And wrap the DynamicPanelSwitcher in a PanelGroupLayout with partialTriggers set to switcher. This should make sure that when the radio group is toggled, the tabbed container changes into an accordion or vice versa.
Toggling the radio button will indeed result in an instant chameleon like reflex in the DynamicPanelSwitcher.
and after toggling an AJAX style partial page refresh: