What is a tag handler ?
·
Let's say that you have created a custom tag,
and you have placed it somewhere in your XHTML page (e.g. <mt:dummy>).
When JSF builds the component tree, it will traverse the page tags, and, at
some moment in time, it will "meet" your tag. This is the time when a
tag handler is executed (if there is a tag handler associated to this tag!). So,
a tag handler is just an optional snippet of code executed when JSF "meets"
a tag (built-in or custom) in the XHTML page.
What is the main goal of a tag handler ?
·
Basically, a tag handler is capable to access
and modify the JSF component tree that is currently built (e.g. modify its
parent). You have to keep in mind that when a tag handler is executed, the
component tree is partially built/restored, which means that some components exist,
while others doesn't exist yet. The tag handler can manipulate the JSF
component tree safely, but you have to pay attention on how you manipulate it.
When you commonly need a custom tag handler
?
·
Is useful when you need to "pinch"
some components from component tree. You can even use it to add/remove
components, or to write your own JSTL-like tags/functions. Moreover, tag handlers can be used to implement validation stuff (e.g. see OmniFaces, <o:validateBean>).
What should I know before start writing a
custom tag handler ?
·
A tag handler extends the TagHandler
class
·
Do not confuse a tag handler with a component
handler! A custom tag handler for a component, converter, validator, or
behavior will extend ComponentHandler, not TagHandler. Tag handlers are
useful only when the component tree is build and they don't correspond to a
component.
·
The tag handler is passed the configuration of
the tag (TagConfig)
- this take place in the tag handler constructor. Among other things, this
configuration contains the Tag object generated by the TagDecorator.
So, the tag handler contains a reference to the Tag instance corresponding
to this TagHandler
instance. Moreover, it provides a shortcut for TagConfig.getTagId(), as tagId.
·
The Tag attributes are available via
getAttribute() and getRequiredAttribute() methods. The later is
for required attributes, and, basically, it invokes getAttribute() and
check if the returned result is null or not. If it is null, then it simply
throw a TagException.
Otherwise, it returns the attribute, which is an instance of TagAttribute
- representation of an XML attribute name=value pair on an XML element in a
Facelet file.
·
JSF signal the fact that it has identified your
tag by calling the tag handler apply() method. This is the "playground"
of a tag handler. Here, you will have access to the FaceletContext and to
the parent component of this tag, so, to the available component tree.
How do I usually write a tag handler
skeleton ?
·
There are several simple steps to follow, and
the first one consist in writing a class that extends the TagHandler.
·
Secondly, you need a constructor with a TagConfig
argument and use this argument to call the super.
·
Third, you need to override the apply()
method - here, you place the code for "pinching" the component tree.
·
Finally, attach the tag handler to the tag name
in a *.taglib.xml
file, and register the *.taglib.xml file in web.xml via javax.faces.FACELETS_LIBRARIES
context parameter.
DummyTagHandler
public class
DummyTagHandler extends TagHandler {
public
DummyTagHandler(TagConfig config) {
super(config);
}
@Override
public void apply(FaceletContext ctx,
UIComponent parent) throws IOException {
// your
job here
}
}
dummy.taglib.xml
...
<namespace>http://jsf.taghandlers.skeleton/dummy</namespace>
<tag>
<tag-name>dummy</tag-name>
<handler-class>jsf.taghandlers.DummyTagHandler</handler-class>
<attribute>
<description>
<![CDATA[
The
text that will be repeated
]]>
</description>
<name>text</name>
<required>true</required>
<type>java.lang.String</type>
</attribute>
<attribute>
<description>
<![CDATA[
The number of times to repeat the text
]]>
</description>
<name>repeat</name>
<required>true</required>
<type>java.lang.String</type>
</attribute>
<attribute>
<description>
<![CDATA[
The
number of times to repeat the text (accepted value: yes)
]]>
</description>
<name>uppercase</name>
<required>false</required>
<type>java.lang.String</type>
</attribute>
</tag>
...
web.xml
...
<context-param>
<param-name>javax.faces.FACELETS_LIBRARIES</param-name>
<param-value>/WEB-INF/dummy.taglib.xml</param-value>
</context-param>
...
Complete
code on GitHub.
Now, let's
see several examples of custom tag handlers:
Text Repeat
For example,
let's suppose that we need the following functionality: we provide a piece of
text, the number of times it should be displayed, and the possibility to be displayed
in uppercase. We may think of a tag as follows:
<mt:textrepeat
text="Vamos Rafa!" repeat="10"
uppercase="yes"/>
We don't
need a custom component for this, because we can simply fix it via a tag
handler, as below:
TextRepeatTagHandler
public class
TextRepeatTagHandler extends TagHandler {
protected final TagAttribute text;
protected final TagAttribute repeat;
protected final TagAttribute uppercase;
public TextRepeatTagHandler(TagConfig config)
{
super(config);
this.text =
this.getRequiredAttribute("text");
this.repeat =
this.getRequiredAttribute("repeat");
this.uppercase =
this.getAttribute("uppercase");
}
@Override
public void apply(FaceletContext ctx,
UIComponent parent) throws IOException {
String s = "";
UIOutput child = new HtmlOutputText();
for (int i = 0; i <
Integer.valueOf(repeat.getValue()); i++) {
s = s + text.getValue() + " ";
}
if (uppercase != null) {
if
(uppercase.getValue().equals("yes")) {
s = s.toUpperCase();
}
else {
s = s.toLowerCase();
}
}
child.setValue(s);
parent.getChildren().add(child);
}
}
Complete
code on GitHub.
Style Input Text
The below TagHandler
set the styleClass
attribute for the parent component, only when the parent component is an
instance of HtmlInputText:
StyleInputTextTagHandler
public class StyleInputTextTagHandler extends TagHandler {
public class StyleInputTextTagHandler extends TagHandler {
public StyleInputTextTagHandler(TagConfig
config) {
super(config);
}
@Override
public void apply(FaceletContext ctx,
UIComponent parent) throws IOException {
if
(parent instanceof HtmlInputText) {
HtmlInputText inputText = (HtmlInputText) parent;
String styleClass =
inputText.getStyleClass();
if (styleClass == null) {
styleClass = "";
}
if
(!styleClass.contains("inputstyle")) {
inputText.setStyleClass(styleClass +
" inputstyle");
}
}
}
}
Complete
code on GitHub
Add Converter/Validator
The below TagHandler
set a converter and a validator for the parent component, only when the parent
component is an instance of HtmlInputText:
ConverterValidatorTagHandler
public class
ConverterValidatorTagHandler extends TagHandler {
public ConverterValidatorTagHandler(TagConfig
config) {
super(config);
}
@Override
public void apply(FaceletContext ctx,
UIComponent parent) throws IOException {
if (parent instanceof HtmlInputText) {
HtmlInputText inputText = (HtmlInputText)
parent;
inputText.addValidator(new
MyValidator());
inputText.setConverter(new
MyConverter());
}
}
}
Complete
code on GitHub.
OmniFaces defines a set of very useful tag handlers. Check the taghandlers section in Showcase!
OmniFaces defines a set of very useful tag handlers. Check the taghandlers section in Showcase!
Niciun comentariu :
Trimiteți un comentariu