Search code examples
htmljsfjsf-2renderer

Custom attributes added by a custom renderer appears at the wrong HTML elements


I had to write a custom renderer for <h:commandLink/> tag to support data-* HTML5 attributes in order to use JSF 2.1 with jQuery mobile framework.

My JSF markup and the output generated by this markup are as follows:

          <h:commandLink value="Prev" data-theme="e" data-role="button" data-inline="true" data-mini="true" data-icon="arrow-l"/>
           <h:commandLink value="Page 1 of 3" data-theme="e" data-role="button" data-inline="true" data-mini="true"/>
           <h:commandLink value="Next" data-theme="e" data-role="button" data-inline="true" data-mini="true" data-icon="arrow-r" data-iconpos="right"/>

enter image description here

It is obvious that my custom renderer properly renders the 2nd and the 3rd <h:commandLink/> tags, but not the 1st one. It seems the data-* attributes belong to the 1st tag is rendered with the immediate parent <div/> tag. This seems a strange (and buggy) behaviour of Mojarra (I use V 2.1.11). Please advice me how to overcome this?

My custom renderer code as follows:

public class MyCommandLinkRenderer extends CommandLinkRenderer {

    @Override
    public void encodeBegin(FacesContext context, UIComponent component) {

        String[] attributes = {"data-theme", "data-role", "data-icon", "data-inline", "data-mini", "data-iconpos"};

        ResponseWriter writer = context.getResponseWriter();
        try {
            for (String attribute : attributes) {
                String value = (String) component.getAttributes().get(attribute);
                if (value != null) {
                    writer.writeAttribute(attribute, value, attribute);
                    System.out.println(attribute + " " + value);
                }
            }
            super.encodeBegin(context, component);
        } catch (Exception e) {
        }
    }
}

Solution

  • You must call super.encodeBegin() before writing the attributes.

    Otherwise you're writing the attributes to the previous HTML element, as confirmed by the generated HTML output. The super.encodeBegin() starts the new HTML element.

    This is just a bug in your own code, not in Mojarra.


    Update: also, overriding the encodeBegin() was not right in first place. You should rather be overriding the LinkRenderer#writeCommonLinkAttributes() method.

    @Override
    protected void writeCommonLinkAttributes(ResponseWriter writer, UIComponent component) throws IOException {
        super.writeCommonLinkAttributes(writer, component);
    
        // ...
    }
    

    It does by the way then not matter if you call super before or after your job.