You may also want to see:
What is the main goal of a renderer ?
· Is responsible to decode the values from the incoming request and to encode the
values to be displayed by transforming the component tree into the HTML markup that
will be displayed to the client machine. Shortly, to transform a JSF component in markup (e.g.
HTML, XML, etc).
When you commonly need a custom renderer ?
· when you need to render a custom component (brand new or
extension of a built-in one), because none of the built-ins do what you want to
achieve
· when you want to alter the look/functionality of an
built-in component
What should I know before start writing a
custom renderer ?
·
mainly you need to know that a renderer extends
directly/indirectly the Renderer class
·
starting with JSF 2.2, you can extend RendererWrapper,
which is a simple implementation of Renderer. JSF 2.2 comes with many wrappers, which are simple implementations of what
they are wrapping, and they help developer to override only the necessary
methods, and to provide specialized behavior to an existing wrapped instance
(e.g. RendererWrapper
can be used for providing specialized behavior to an existing Renderer
instance). The wrapped instance is available via the getWrapped() method.
·
The main three methods of a Renderer
are encodeBegin(),
encodeChildren()
and encodeEnd().
By default, JSF calls them in this order, and the first one usually renders the
beginning of the markup - "open" tags (e.g. <head>, <input>,
<form>,
etc), the second one renders the
children (this is configurable via getRendersChildren() flag), and the last
one is ending the markup - "close" tags (e.g. </head>, </input>,
</form>).
·
in order to link a component with a renderer,
you should know how to work with the UIComponent.setRenderType() method and
with the component-family,
component-type
and renderer-type
artifacts as level of annotations or with the <render-kit> and <renderer> tags in faces-config.xml.
A detailed dissertation about them is the OmniFacesCustom Components Approach post. OmniFaces provides solid techniques for
writing custom components and renderers!
How do I usually write a Renderer skeleton
?
·
when you write a brand new component (extending UIComponentBase),
you will extend the Renderer class directly and override most of its methods.
Usually in these cases you will link the custom component with the renderer via
annotations and setRendererType()
method.
DummyComponent
@FacesComponent(value
= DummyComponent.COMPONENT_TYPE, createTag = true)
public class
DummyComponent extends UIComponentBase {
public static final String COMPONENT_FAMILY =
"jsf.components";
public static final String COMPONENT_TYPE =
"jsf.components.DummyComponent";
public DummyComponent() {
setRendererType(DummyRenderer.RENDERER_TYPE);
}
@Override
public String getFamily() {
return
COMPONENT_FAMILY;
}
}
DummyRenderer
@FacesRenderer(componentFamily
= DummyComponent.COMPONENT_FAMILY, rendererType = DummyRenderer.RENDERER_TYPE)
public class
DummyRenderer extends Renderer {
public static final String RENDERER_TYPE =
"jsf.renderers.DummyRenderer";
private static final Logger LOG =
Logger.getLogger(DummyRenderer.class.getName());
public DummyRenderer(){
}
@Override
public Object getConvertedValue(FacesContext
context, UIComponent component, Object submittedValue) throws
ConverterException {
return
super.getConvertedValue(context, component, submittedValue);
}
@Override
public boolean getRendersChildren() {
return
super.getRendersChildren();
}
@Override
public String convertClientId(FacesContext
context, String clientId) {
return super.convertClientId(context,
clientId);
}
@Override
public void encodeBegin(FacesContext context,
UIComponent component) throws IOException {
LOG.info("--- Rendering DummyComponent -
encodeBegin() method ---");
super.encodeBegin(context, component);
}
@Override
public void encodeChildren(FacesContext
context, UIComponent component) throws IOException {
LOG.info("--- Rendering DummyComponent -
encodeChildren() method ---");
super.encodeChildren(context, component);
}
@Override
public void encodeEnd(FacesContext context,
UIComponent component) throws IOException {
LOG.info("--- Rendering DummyComponent -
encodeEnd() method ---");
super.encodeEnd(context, component);
}
@Override
public void decode(FacesContext context,
UIComponent component) {
LOG.info("--- Rendering DummyComponent -
decode() method ---");
super.decode(context, component);
}
}
·
when you write a custom component which extends a
built-in component, you usually extend the renderer of the built-in component
also - directly (most probably) or indirectly. Usually in these cases you will link
the custom component with the renderer via the setRenderType() method. Of
course, you can also use the renderer of the built-in component without any modifications.
e.g. Create and render a component that extends the UIOutput - GitHubComplete Code [CustomRendererSkeleton_2]
DummyComponent
@FacesComponent(createTag
= true)
public class
DummyComponent extends UIOutput {
// inherits the UIOutput component-family:
javax.faces.Output
//default renderer-type: javax.faces.Text is
replaced by DummyRenderer
public DummyComponent() {
setRendererType(DummyRenderer.RENDERER_TYPE);
}
}
DummyRenderer
@FacesRenderer(componentFamily
= DummyComponent.COMPONENT_FAMILY, rendererType = DummyRenderer.RENDERER_TYPE)
public class
DummyRenderer extends TextRenderer {
private static final Logger LOG =
Logger.getLogger(DummyRenderer.class.getName());
public static final String RENDERER_TYPE =
"jsf.renderers.DummyRenderer";
public DummyRenderer(){
}
@Override
public void encodeChildren(FacesContext
context, UIComponent component) throws IOException {
LOG.info("--- Rendering DummyComponent -
encodeChildren() method ---");
super.encodeChildren(context, component); //
calls the TextRenderer#encodeChildren()
}
@Override
public boolean getRendersChildren() {
LOG.info("--- Rendering DummyComponent -
getRendersChildren() method ---");
return super.getRendersChildren(); // calls
the TextRenderer#getRendersChildren()
}
@Override
protected void getEndTextToRender(FacesContext
context, UIComponent component, String currentValue) throws IOException {
LOG.info("--- Rendering DummyComponent -
getEndTextToRender() method ---");
super.getEndTextToRender(context, component,
currentValue); // calls the TextRenderer#getEndTextToRender()
}
@Override
public void encodeBegin(FacesContext context,
UIComponent component) throws IOException {
LOG.info("--- Rendering DummyComponent -
encodeBegin() method ---");
super.encodeBegin(context,
component); // calls the TextRenderer#encodeBegin()
}
}
·
when you just want to alter a built-in
component at rendering level, you usually extend the built-in renderer and you instruct JSF to use
your renderer instead of the default one via faces-config.xml, <renderer>
tag.
e.g. Use a
custom Renderer for the JSF UIOutput component, GitHub Complete Code [CustomRendererSkeleton_3]
DummyRenderer
public class
DummyRenderer extends TextRenderer {
private static final Logger LOG =
Logger.getLogger(DummyRenderer.class.getName());
public static final String RENDERER_TYPE =
"jsf.renderers.DummyRenderer";
public DummyRenderer(){
}
@Override
public void encodeChildren(FacesContext
context, UIComponent component) throws IOException {
LOG.info("--- Rendering DummyComponent -
encodeChildren() method ---");
super.encodeChildren(context, component); //
calls the TextRenderer#encodeChildren()
}
@Override
public boolean getRendersChildren() {
LOG.info("--- Rendering DummyComponent -
getRendersChildren() method ---");
return super.getRendersChildren(); // calls
the TextRenderer#getRendersChildren()
}
@Override
protected void getEndTextToRender(FacesContext
context, UIComponent component, String currentValue) throws IOException {
LOG.info("--- Rendering DummyComponent -
getEndTextToRender() method ---");
super.getEndTextToRender(context, component,
currentValue); // calls the TextRenderer#getEndTextToRender()
}
@Override
public void encodeBegin(FacesContext context,
UIComponent component) throws IOException {
LOG.info("--- Rendering DummyComponent -
encodeBegin() method ---");
super.encodeBegin(context, component); //
calls the TextRenderer#encodeBegin()
}
}
faces-config.xml
...
<application>
<render-kit>
<renderer>
<component-family>javax.faces.Output</component-family>
<renderer-type>javax.faces.Text</renderer-type>
<renderer-class>jsf.renderers.DummyRenderer</renderer-class>
</renderer>
</render-kit>
</application>
...
Niciun comentariu :
Trimiteți un comentariu