JavaServer faces (JSF) provides scalable component models that developers can create reusable components and use these custom components to improve development efficiency and reduce development costs. Although the JSF component model is very powerful for customization and reuse, developers generally think that developing JSF custom components is not easy, because at least the internal mechanisms of JSF encode/decode and State holder need to be familiar with and overwrite the corresponding methods (refer to "Skeptical JSF: JSF component development"), as described in encodebegine (), decode (), savestate (), and restorestate (). For developing complex custom components, you even need to have a deep understanding of more interfaces, such as namingcontainer, stateholder, editablevalueholder, and actionsource.
However, reusing JSF standard components greatly simplifies the development of custom components, especially for custom composite components. In most cases, we can reuse the standard Renderer, State management, event listener, converter, and validators that the JSF framework already provides. There are few articles or books about how to reuse these standard functions. This article proposes the Principles and Techniques for developing JSF custom composite components quickly based on the Reuse Policy.
This article first summarizes the general principles of JSF component development, and then explains which standard functions can be reused and how to reuse them through the development of a value scroller custom composite component, to simplify the development of JSF custom composite components. Principles and skills
There are two main principles for developing JSF custom composite components: reusing existing standard components and ensuring that custom components are easy to reuse.
1. Reuse the functions and implementations of standard components as much as possible
We recommend that you fully implement the encode/decode logic for the traditional custom composite component development. However, this is time-consuming and error-prone. There is no doubt that we can reduce or even skip writing this part of code by reusing the Renderer of standard components. In addition, to achieve flexible configuration and use, custom composite components usually need to provide many attributes. We need to write a lot of code to process the read/write and State management of these attributes. In fact, we can simply pass the attributes of a custom composite component to its own standard components, and the existing standard code processes these attributes without repeating the code.
2. clearly separate component classes, label classes, and model classes
The component model of JSF is recommended to have clear responsibility distribution between component classes, tag classes, and model classes for reuse and extension. The component class should not depend on the javax.faces.component.html package, because the component class can be used not only in HTML, but also in other Markup languages (such as WML ). That is to say, the component class should not directly reference the HTML components in the javax.faces.component.html package. For example, creating an htmlcommandbutton instance in your component class is not advisable. You should consider using the uicommand in the javax. Faces. Component package. On the other hand, if you want your model class to be reused in different Web frameworks, your model class should not depend on any JSF package, that is, the model class only indicates the Business Object and does not contain any components, data, and status related to the user interface.
Based on these principles, we can compare the traditional methods with the techniques described in this article. We can find that the reuse-based development strategy greatly simplifies the compilation of JSF custom composite components. Generally, the following three steps are required to develop a JSF custom component (refer to the "Skeptical JSF: JSF component development ").
1. Extended uicomponent
· Traditional Method: create a class, extend uicomponent, save component Status, register component in faces-config.xml
Reuse tips:
· Select uipanel as the layout container and reuse the standard component as the child component of the composite component.
· Implement the internal action listener.
2. Define the Renderer or Inline implement it
Traditional Method: override implement encode/decode, register Renderer in faces-config.xml.
Reuse tips: reuse standard Renderer types.
3. Create a custom tag that inherits the uicomponenttag
Traditional Method: return the Renderer type and component type, and set the JSF expression attribute.
Reuse tips: Pass attribute values to the standard component that serves as a child component. Overview
The development steps of a custom composite component value scroller demonstrate how to reuse the functions and implementations of standard components using a variety of techniques to simplify development and make it easy to reuse. Value scroller allows you to enter a value by clicking the value-added or impairment button instead of manually typing it, as shown in figure 1. This example only contains the most basic functions. For example, only integer value input is supported, but the content described in this article is sufficient.
Figure 1. Value scroller on the test page
Figure 2 illustrates the basic class structure of value scroller, which follows the MVC pattern. The component class valuescroller extends uipanel and acts as a controller to interact with users. The tag class valuescrollertag inherits the uicomponenttag and is displayed on the view page. The value object bound to the value scroroller serves as a model to store the value you type.
Figure 2. Class Structure of value scroller
In the subsequent sections, this article describes how to quickly develop a JSF custom composite component by applying the principles and techniques mentioned above with the value scroller example. Select uipanel as container
The first step to creating a JSF custom composite component is to select a standard component class for extension. We usually consider using this component class as a container and embedding it into sub-components to form a composite component. Here, we choose to inherit uipanel as the container of value scroller, render the page as a grid, and include a uiinput and two uicommands, respectively as the value input box and the add-on button, as shown in Listing 1:
List 1. Extended class uipanel
Public class valuescroller extends uipanel {
/**
* The default constructor
*
*/
Public valuescroller (){
Super ();
Addchildrenandfaces ();
}
}
The standard components that serve as the value scroroller child components will be added to the layout container in the addchildrenandfaces method. Reuse standard Renderer type
Next, we start to create the child component of value scroller and implement the Renderer function. In the traditional method, the encodebegin () and decode () Methods of uicomponent must be overwritten. However, if the composite component we developed is composed of multiple standard components, we can add standard component base classes that do not depend on specific Markup languages to custom components and set a standard Renderer type for each standard component, you can complete the Renderer function to be implemented by the composite component. The advantages of reusing the standard component Renderer are that it reduces the development workload and the chance of errors, which is especially important for beginners of JSF; it does not need to implement the encode/decode logic related to a specific markup language, makes component classes easier to reuse.
This book titled "practice with JavaServer faces" lists the standard Renderer types provided by the JSF specifications.
Table 1. JSF standard Renderer
As shown in table 1, a component base class usually corresponds to multiple Renderer types (if HTML is used as the markup language, it corresponds to multiple HTML elements ), because the component base class only defines general data and behavior. For example, uicommand has two HTML subclasses: htmlcommandbutton and htmlcommandlink, which correspond to the Renderer types javax. Faces. Link and javax. Faces. Button respectively. To include a link in a composite component, you only need to create a uicommand instance and set its Renderer type to javax. faces. instead of overwriting the encodebegin () and decode () methods. Listing 2 lists how child components in value scroller are created in the component class valuescroller and how attributes such as Renderer are set.
Listing 2. Reuse the standard Renderer to create a custom composite component
Private Static final string panel_grid_renderer = "javax. Faces. Grid ";
Private Static final string input_text_renderer = "javax. Faces. Text ";
Private Static final string command_link_renderer = "javax. Faces. Link ";
Private Static final string graphic_image_renderer = "javax. Faces. Image ";
/**
* Add children to the base container
*
*/
Private void addchildrenandfaces (){
// Set attributes of the base container
This. setrenderertype (panel_grid_renderer );
This. getattributes (). Put (columns_attribute, new INTEGER (2 ));
// Add the input component
Input = new uiinput ();
Input. setid (input_id );
Input. setrenderertype (input_text_renderer );
This. getchildren (). Add (input );
// Add the container of the up and down links
Uipanel linkcontainer = new uipanel ();
Linkcontainer. setid (linkpanel_id );
Linkcontainer. setrenderertype (panel_grid_renderer );
Linkcontainer. getattributes (). Put (columns_attribute, new INTEGER (1 ));
Scrolleractionlistener listener = new scrolleractionlistener ();
// Add the up link
Uicommand uplink = new uicommand ();
Uplink. setid (uplink_id );
Uplink. setrenderertype (command_link_renderer );
Uplink. addactionlistener (listener );
Uigraphic upimage = new uigraphic ();
Upimage. setid (upimage_id );
Upimage. setrenderertype (graphic_image_renderer );
Upimage. seturl (upimage_url );
Uplink. getchildren (). Add (upimage );
Linkcontainer. getchildren (). Add (uplink );
// Add the down Link
Uicommand downlink = new uicommand ();
Downlink. setid (downlink_id );
Downlink. setrenderertype (command_link_renderer );
Downlink. addactionlistener (listener );
Uigraphic downimage = new uigraphic ();
Downimage. setid (downimage_id );
Downimage. setrenderertype (graphic_image_renderer );
Downimage. seturl (downimage_url );
Downlink. getchildren (). Add (downimage );
Linkcontainer. getchildren (). Add (downlink );
This. getchildren (). Add (linkcontainer );
}
Pass attribute values to standard components
Let's take a look at the attributes defined in the tag description file (TLD.
Listing 3. Defining attributes of a custom composite component in TLD
<Tag>
<Name> valuescroller </Name>
<Tag-class> component. taglib. valuescrollertag </Tag-class>
<Body-content> JSP </body-content>
<Attribute>
<Name> id </Name>
<Description> valuescroller id </description>
</Attribute>
<Attribute>
<Name> value </Name>
<Description> valuescroller value </description>
</Attribute>
<Attribute>
<Name> size </Name>
<Description> input field size </description>
</Attribute>
<Attribute>
<Name> min </Name>
<Description> Minimum value </description>
</Attribute>
<Attribute>
<Name> max </Name>
<Description> maximum value </description>
</Attribute>
<Attribute>
<Name> step </Name>
<Description> scrolling step </description>
</Attribute>
</Tag>
We can see that, except that min/max/step is a custom attribute, all other attributes belong to the JSF standard component and can be passed directly to the standard component processing that constitutes the value scroller, you do not need to overwrite the implementation methods savestate () and restorestate () for the properties of these standard components ().
There are usually two ways to pass attribute values. When you need to perform some extra operations on attributes (such as verification or conversion), you can pass the attributes to the custom component class in the tag class valuescrollertag, as shown below:
Listing 4. Passing Custom Attributes
/**
* Override the setproperties Method
*/
Protected void setproperties (uicomponent component ){
Super. setproperties (component );
Valuescroller Vs = (valuescroller) component;
Application APP = facescontext. getcurrentinstance (). getapplication ();
// Set Value Attribute
If (value! = NULL ){
If (isvaluereference (string) value )){
Valuebinding VB = app. createvaluebinding (string) value );
Vs. setvaluebinding ("value", VB );
} Else {
Throw new illegalargumentexception ("the value property must be a value" +
"Binding expression that points to a bean property .");
}
}
// Set ID attribute
If (ID! = NULL ){
Vs. setid (string) ID );
}
// Set other attributes
Vs. setmin (min );
Vs. setmax (max );
Vs. setstep (STEP );
}
Another method is to directly add the attribute values to the attribute map of the corresponding standard component in the tag class valuescrollertag. For example, pass the size attribute to the uiinput contained in the Custom composite component:
Listing 5. Passing standard attributes
Vs. findcomponent ("input"). getattributes (). Put ("size", new INTEGER (size); implement internal action listener
In value scroller, click the value-added or impairment button to increase or decrease the value in the input box. We can simply implement an internal action listener in the valuescroller component class and reuse the event processing logic of uicommand.
Listing 6. Implementing the value scroller action listener
/**
* Internal action listener for value scroller
* _ Cnnew1 @ author CLL
*
*/
Private class scrolleractionlistener implements actionlistener {
Public void processaction (actionevent e ){
// Only integer is supported for this demo
If (input. getvalue () instanceof integer ){
String commandid = (uicommand) E. getsource (). GETID ();
Int value = (integer) Input. getvalue (). intvalue ();
// Increase value if the up link is clicked
If (commandid. Equals (uplink_id )){
If (Value + getstep ()> MAX ){
Input. setvalue (New INTEGER (max ));
} Else {
Input. setvalue (New INTEGER (Value + getstep ()));
}
}
// Decrease value if the down link is clicked
Else if (commandid. Equals (downlink_id )){
If (value-getstep () <min ){
Input. setvalue (New INTEGER (min ));
} Else {
Input. setvalue (New INTEGER (value-getstep ()));
}
}
} Else {
Throw new illegalargumentexception (
"Unsupported binding type," +
"And only integer instance allowed for this demo .");
}
}
}
Finally, when the addchildrenandfaces method is called to create and add sub-components, add the Custom Action listener to the value-added and impairment components.
Listing 7. register the value scroller action listener
Scrolleractionlistener listener = new scrolleractionlistener ();
Uplink. addactionlistener (listener );
Downlink. addactionlistener (listener );
Use Value scroller
Value scroller development has been completed, the use is also very simple, first declare reference value scroller in the faces-config.xml, as shown below:
Listing 8. Declaring reference value scroller in faces-config.xml
<Component>
<Component-type> XYZ. valuescroller </component-type>
<Component-class>
Component. valuescroller
</Component-class>
</Component>
Then, the test. jsp page contains the label description file of value scroller.
Listing 9. TLD containing value scroller in JSP
<% @ Taglib uri = "/WEB-INF/lib/valuescroller. TLD" prefix = "XYZ" %>
Finally, use the value scroler label on the test. jsp page, specify the size/MIN/max/step attribute value, and deploy and run the label. The result shown in Figure 1 is displayed.
Listing 10. Create value scroller in JSP and set attributes
<XYZ: valuescroller value = "# {pc_test.itemcount}" size = "5" min = "-50" max = "10000" step = "2">
</XYZ: valuescroller>
Conclusion
We can see that the implementation of the custom composite component value scroller described in this article does not write the logic related to encode/decode and State/event management, which is simple, fast, and easy to reuse. The development skills of the JSF custom composite components summarized in this article greatly reduce the complexity and workload, and are better than the traditional development methods.
Author:IBM Chen Lilong ealine Zhan zikko