I am trying to write a renderer which would process the placeholder
attribute on an <h:inputText>
component.
I headed to this path after reading JSF 2.0 strips out needed HTML5 attributes and it seems correct. Here's my custom renderer
public class InputRenderer extends com.sun.faces.renderkit.html_basic.TextRenderer{
@Override
public void encodeBegin(FacesContext context, UIComponent component)
throws IOException {
System.out.println("Rendering :"+component.getClientId());
String placeholder = (String)component.getAttributes().get("placeholder");
if(placeholder != null) {
ResponseWriter writer = context.getResponseWriter();
writer.writeAttribute("placeholder", placeholder, "placeholder");
}
super.encodeBegin(context, component);
}
@Override
public void decode(FacesContext context, UIComponent component) {
super.decode(context, component);
}
@Override
public void encodeEnd(FacesContext context, UIComponent component)
throws IOException {
super.encodeEnd(context, component);
}
}
And this renderer is registered in faces config as
<render-kit>
<renderer>
<component-family>javax.faces.Input</component-family>
<renderer-type>javax.faces.Text</renderer-type>
<renderer-class>com.example.renderer.InputRenderer</renderer-class>
</renderer>
</render-kit>
This gets registered fine, no issues there.
My intention is to process the placeholder
attribute, insert it, and then delegate the processing to super. My above code doesn't work because I'm inserting the attribute at a wrong place. It must be inserted after writer.startElement('input')
has executed. However, the startElement must be happening somewhere in the super's encodeBegin()
method. So how do I insert a custom attribute ('placeholder' in this case) and then continue the execution flow?
NB: The above code does add a placeholder
attribute but not to the input component that I intend to, It writes it to the parent of the Input (since I'm trying to write an attribute before the component itself is actually written in the stream, it applies the attribute to the current component)
And to override for MyFaces 2.0.8+
package com.hsop.abc.eld;
import java.io.IOException;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import org.apache.myfaces.renderkit.html.HtmlTextRenderer;
public class InputRenderer extends HtmlTextRenderer
{
@Override
protected void renderInputBegin(FacesContext context, UIComponent component)
throws IOException
{
// TODO Auto-generated method stub
super.renderInputBegin(context, component);
Object placeholder = component.getAttributes().get("placeholder");
if(placeholder != null) {
ResponseWriter writer = context.getResponseWriter();
writer.writeAttribute("placeholder", placeholder, "placeholder");
}
}
}
This is my way. I added placeholder and data-theme attributes. If you want to add more attributes, you should just add its name to attributes array.
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import com.sun.faces.renderkit.html_basic.TextRenderer;
public class InputRender extends TextRenderer {
@Override
protected void getEndTextToRender(FacesContext context,
UIComponent component,
String currentValue)
throws java.io.IOException{
String [] attributes = {"placeholder","data-theme"};
ResponseWriter writer = context.getResponseWriter();
for(String attribute : attributes)
{
String value = (String)component.getAttributes().get(attribute);
if(value != null) {
writer.writeAttribute(attribute, value, attribute);
}
}
super.getEndTextToRender(context, component, currentValue);
}
}
You should add this to faces-config.xml file.
<render-kit>
<renderer>
<component-family>javax.faces.Input</component-family>
<renderer-type>javax.faces.Text</renderer-type>
<renderer-class>your.package.InputRenderer</renderer-class>
</renderer>
</render-kit>
You can just override ResponseWriters startElement method, that method is only called once and then you can restore to the original responsewriter object.
import javax.faces.context.*;
import java.io.IOException;
public class InputRenderer extends com.sun.faces.renderkit.html_basic.TextRenderer{
// Put all of the attributes you want to render here...
private static final String[] ATTRIBUTES = {"required","placeholder"};
@Override
protected void getEndTextToRender(FacesContext context,
UIComponent component, String currentValue) throws IOException {
final ResponseWriter originalResponseWriter = context.getResponseWriter();
context.setResponseWriter(new ResponseWriterWrapper() {
@Override
// As of JSF 1.2 this method is now public.
public ResponseWriter getWrapped() {
return originalResponseWriter;
}
@Override
public void startElement(String name, UIComponent component)
throws IOException {
super.startElement(name, component);
if ("input".equals(name)) {
for (String attribute : ATTRIBUTES)
{
Object value = component.getAttributes().get(attribute);
if (value != null)
{
super.writeAttribute(attribute,value,attribute);
}
}
}
});
super.getEndTextToRender(context, component, currentValue);
context.setResponseWriter(originalResponseWriter); // Restore original writer.
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With