Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding custom attribute (HTML5) support to JSF 2.0 UIInput component

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)

like image 956
Niks Avatar asked Jul 28 '11 13:07

Niks


3 Answers

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");
    }

    }
}
like image 124
Madhuri Avatar answered Oct 22 '22 14:10

Madhuri


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>
like image 31
mb.akturk Avatar answered Oct 22 '22 13:10

mb.akturk


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.
    }



}
like image 7
dileks Avatar answered Oct 22 '22 12:10

dileks