A while ago I developed some HTML5 XPages applications, but the development process was a little bit frustrating because of the missing possibility to add empty attributes to a PassThroughTag. A single empty attribute is not allowed, because this would result in invalid XML, and you cannot use „xp:attributes“ with „UIPassThroughTag“ components.
A simple example like this…
<input type="text" value="John Doe" disabled />
… always ended up in something like this:
<xp:text tagName="input" disableTheme="true">
<xp:this.attrs>
<xp:attr name="disabled" minimized="true" value="disabled" />
<xp:attr name="value" value="John Doe" />
</xp:this.attrs>
</xp:text>
To fit my requirements, I had extended the „UIPassThroughTag“ with a special attribute named „emptyAttrs„. This allowed me to write the example above in the following syntax:
<input type="text" value="John Doe" emptyAttrs="disabled" />
(Multiple empty attributes can be added comma separated.)
Here is the Java class:
package ch.hasselba.xpages;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import com.ibm.xsp.component.UIPassThroughTag;
import com.ibm.xsp.renderkit.html_basic.PassThroughTagRenderer;
import com.ibm.xsp.webapp.XspHttpServletResponse;
public class PassThroughTagRendererEx extends PassThroughTagRenderer {
private final static String EMPTY_ATTRIBUTE_NAME = "emptyAttrs";
public void encodeBegin(FacesContext fc, UIComponent uiComponent)
throws IOException {
// Component is rendered?
if (uiComponent.isRendered() == false) {
return;
}
// only process instances of UIPassThroughTags
if ((uiComponent instanceof UIPassThroughTag)) {
UIPassThroughTag uiPTTag = (UIPassThroughTag) uiComponent;
ResponseWriter rw = fc.getResponseWriter();
// grab the printer writer directly from the response
XspHttpServletResponse response = (XspHttpServletResponse)
fc.getExternalContext().getResponse();
PrintWriter rwPrinter = response.getWriter();
// start the element tag
rw.startElement(uiPTTag.getTag(), uiComponent);
// process all attributes
List<UIPassThroughTag.TagAttribute> attrList = uiPTTag
.getTagAttributes();
if (attrList != null) {
UIPassThroughTag.TagAttribute tagAttribute = null;
String attrName = null;
String attrValue = null;
for (int i = 0; i < attrList.size(); i++) {
tagAttribute = attrList.get(i);
attrName = tagAttribute.getName();
attrValue = tagAttribute.getValue();
if (EMPTY_ATTRIBUTE_NAME.equalsIgnoreCase(attrName)) {
// process all empty tags
String tags[] = attrValue.split(",");
for( int j=0; j<tags.length; j++){
// write the attribute name only
rwPrinter.write( " " );
rwPrinter.write( tags[j].trim() );
}
}else{
// write the attribute data
rw.writeAttribute(attrName, attrValue, null);
}
}
}
} else {
// process other instances "as always"
super.encodeBegin(fc, uiComponent);
}
}
}
To activate the class you have to overwrite the renderer in the „faces-config.xml„:
<?xml version="1.0" encoding="UTF-8"?>
<faces-config>
<render-kit>
<renderer>
<component-family>javax.faces.Output</component-family>
<renderer-type>com.ibm.xsp.PassThroughTagRenderer</renderer-type>
<renderer-class>ch.hasselba.xpages.PassThroughTagRendererEx</renderer-class>
</renderer>
</render-kit>
</faces-config>
Ok, I quit, I was just trying to paste the code of a xp:text wich returns HTML (escape=“false“) and here you have 100% control of the HTML rendered, and empty attributes can be used.
You can remove the spam comments 🙂
Thanks for your comments 🙂
Sure, you can also do this. But it is just a crutch, and makes plain HTML code not easier to read / maintain. And you have to disable the theme again for every component, or there is a span tag around your html element.