I read on some blogs or articles, that some guys compare the (class
based) renderers for the JSF UI components with the *good old* servlet
time :-)
This statement has a truth, with the current renderers we have (again)
the process of rendering markup (e.g. HTML) inside of clazzes.
If you want to change the behaivor/look-and-feel of the UI component
you have to create a custom renderer that fits your needs.
So why not using a template language for rendering, that is easy to
read and to maintain?
In my current day job, I have worked (a bit) with Jakarta Velocity and
I like what I saw there. So I just decided to create a Renderer that
uses a velocity template to renderer the content of a JSF component.
Note, that this is a very simple renderer in its first version. But
I'll look again at this stuff. This is just to share my
results/toughts...
I started with the simplest renderer out there in jsf space. The
renderer for the HtmlOutputText component.
I have a simple jsf file like:
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<f:view>
<h:outputText value="Hello
JSF" style="color:red"/>
</f:view>
Also my template is very simple. I focused only on value and style
attribute. Here the
template goes:
<span
#if($component.style) style="$component.style" #end
onclick="alert('Velocity rendered');">#if ($component.value) $component.value
#end</span>
that's all.
Keep in mind, that no style attribute is rendered, if you haven't set
the style attribute in your JSF page.
However, my VelocityRenderer for the <h:outputText> looks like:
package
net.wessendorf.jsf.renderer;
import
java.io.IOException;
import
java.io.StringWriter;
import
java.util.Properties;
import
javax.faces.component.UIComponent;
import
javax.faces.context.FacesContext;
import
javax.faces.context.ResponseWriter;
import
javax.faces.render.Renderer;
import
org.apache.velocity.Template;
import
org.apache.velocity.VelocityContext;
import
org.apache.velocity.app.Velocity;
import
org.apache.velocity.exception.MethodInvocationException;
import
org.apache.velocity.exception.ParseErrorException;
import
org.apache.velocity.exception.ResourceNotFoundException;
public class
VelocityRenderer extends Renderer {
private VelocityContext ctx = null;
private Template template = null;
private StringWriter sw = null;
public VelocityRenderer() {
super();
Properties p = new Properties();
p.put("file.resource.loader.path", "/home/matzew/");
try {
Velocity.init(p);
} catch (Exception e) {
e.printStackTrace();
}
ctx = new VelocityContext();
}
public void encodeEnd(FacesContext context, UIComponent component)
throws IOException {
try {
template =
Velocity.getTemplate("htmltext.vm");
} catch (ResourceNotFoundException e) {
e.printStackTrace();
} catch (ParseErrorException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
sw = new StringWriter();
ctx.put("component", component);
try {
template.merge(ctx, sw);
} catch (ResourceNotFoundException e) {
e.printStackTrace();
} catch (ParseErrorException e) {
e.printStackTrace();
} catch (MethodInvocationException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
ResponseWriter writer = context.getResponseWriter();
writer.write(sw.toString());
sw = null;
}
}
Here is the place where Velocity meets JSF ;) The construtor is called
on startup of the web app, since the Renderers are singletons. Here is
the place we init the Velocity infrastructure. The Velocity.init()
takes some properties. In my
case I told it where my templates are located. Yes... that place is
hard coded currently...
The encodeEnd() is the place where the
rendering will be done. I lookup the template for the
<h:outputText> component. Velocity uses
a StringWriter to merge the templates. After
creating the StringWriter I
put the UI component to the VelocityContext and call now merge() method
on the template. The calculated
result I put to the JSF ResponseWriter object. The reason for putting
the component into the context is that you now
can access all attributes for the JSF UI component in your template to
render it (e.g. style, id, ...).
To enable this (plain) renderer you have to configure it. In my
faces-config.xml I made this statement:
<faces-config>
...
<render-kit>
<renderer>
<component-family>javax.faces.Output</component-family>
<renderer-type>javax.faces.Text</renderer-type>
<renderer-class>
net.wessendorf.jsf.renderer.VelocityRenderer
</renderer-class>
</renderer>
</render-kit>
...
</faces-config>
That's all.
If you now start your web container and surf to the page you'll see the
rendered markup. Clicking on the rendered text will cause the java
script 'alert()' method.
On runtime you are now able to change the behaivor like:
<h3 #if($component.style) style="$component.style" #end>#if ($component.value) $component.value
#end</h3>
After editing you'll see a red headline instead.
I hope this informations are valuable for you. My next step will be
creating of a *better* designed renderer and creating a template for
the dataTable component.