{"id":2049,"date":"2015-03-10T22:26:46","date_gmt":"2015-03-10T20:26:46","guid":{"rendered":"http:\/\/hasselba.ch\/blog\/?p=2049"},"modified":"2015-03-10T22:28:05","modified_gmt":"2015-03-10T20:28:05","slug":"xpages-empty-html5-attibutes-passthroughtags","status":"publish","type":"post","link":"https:\/\/hasselba.ch\/blog\/?p=2049","title":{"rendered":"XPages: Empty HTML5 Attibutes &#038; PassThroughTags"},"content":{"rendered":"<p>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.\u00a0 A single empty attribute is not allowed, because this would result in invalid XML, and you cannot use &#8222;<em>xp:attributes<\/em>&#8220; with &#8222;<em>UIPassThroughTag<\/em>&#8220; components.<\/p>\n<p>A simple example like this&#8230;<\/p>\n<pre><code>&lt;input type=\"text\" value=\"John Doe\" disabled \/&gt;<\/code><\/pre>\n<p>&#8230; always ended up in something like this:<\/p>\n<pre><code>&lt;xp:text tagName=\"input\" disableTheme=\"true\"&gt;\r\n   &lt;xp:this.attrs&gt;\r\n      &lt;xp:attr name=\"disabled\" minimized=\"true\" value=\"disabled\" \/&gt;\r\n      &lt;xp:attr name=\"value\" value=\"John Doe\" \/&gt;\r\n   &lt;\/xp:this.attrs&gt;\r\n&lt;\/xp:text&gt;<\/code><\/pre>\n<p>To fit my requirements, I had extended the &#8222;<em>UIPassThroughTag<\/em>&#8220; with a special attribute named &#8222;<em>emptyAttrs<\/em>&#8222;. This allowed me to write the example above in the following syntax:<\/p>\n<pre><code>&lt;input type=\"text\" value=\"John Doe\" emptyAttrs=\"disabled\" \/&gt;<\/code><\/pre>\n<p>(Multiple empty attributes can be added comma separated.)<\/p>\n<p>Here is the Java class:<\/p>\n<pre><code>package ch.hasselba.xpages;\r\n\r\nimport java.io.IOException;\r\nimport java.io.PrintWriter;\r\nimport java.util.List;\r\n\r\nimport javax.faces.component.UIComponent;\r\nimport javax.faces.context.FacesContext;\r\nimport javax.faces.context.ResponseWriter;\r\n\r\nimport com.ibm.xsp.component.UIPassThroughTag;\r\nimport com.ibm.xsp.renderkit.html_basic.PassThroughTagRenderer;\r\nimport com.ibm.xsp.webapp.XspHttpServletResponse;\r\n\r\npublic class PassThroughTagRendererEx extends PassThroughTagRenderer {\r\n\r\n\u00a0\u00a0 \u00a0private final static String EMPTY_ATTRIBUTE_NAME = \"emptyAttrs\";\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0public void encodeBegin(FacesContext fc, UIComponent uiComponent)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0throws IOException {\r\n\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\/\/ Component is rendered?\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0if (uiComponent.isRendered() == false) {\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0return;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\/\/ only process instances of UIPassThroughTags\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0if ((uiComponent instanceof UIPassThroughTag)) {\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0UIPassThroughTag uiPTTag = (UIPassThroughTag) uiComponent;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0ResponseWriter rw = fc.getResponseWriter();\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\/\/ grab the printer writer directly from the response\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0XspHttpServletResponse response = (XspHttpServletResponse) \r\n                fc.getExternalContext().getResponse();\r\n\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0PrintWriter rwPrinter = response.getWriter();\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\/\/ start the element tag\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0rw.startElement(uiPTTag.getTag(), uiComponent);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\/\/ process all attributes\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0List&lt;UIPassThroughTag.TagAttribute&gt; attrList = uiPTTag\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0.getTagAttributes();\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0if (attrList != null) {\r\n\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0UIPassThroughTag.TagAttribute tagAttribute = null;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0String attrName = null;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0String attrValue = null;\r\n\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0for (int i = 0; i &lt; attrList.size(); i++) {\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0tagAttribute = attrList.get(i);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0attrName = tagAttribute.getName();\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0attrValue = tagAttribute.getValue();\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0if (EMPTY_ATTRIBUTE_NAME.equalsIgnoreCase(attrName)) {\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\/\/ process all empty tags\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0String tags[] = attrValue.split(\",\");\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0for( int j=0; j&lt;tags.length; j++){\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\/\/ write the attribute name only\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0rwPrinter.write( \" \" );\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0rwPrinter.write( tags[j].trim() );\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0}else{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\/\/ write the attribute data\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0rw.writeAttribute(attrName, attrValue, null);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0} else {\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\/\/ process other instances \"as always\"\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0super.encodeBegin(fc, uiComponent);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0}\r\n\r\n}\r\n<\/code><\/pre>\n<p>To activate the class you have to overwrite the renderer in the &#8222;<em>faces-config.xml<\/em>&#8222;:<\/p>\n<pre><code>&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\r\n&lt;faces-config&gt;\r\n\u00a0 &lt;render-kit&gt;\r\n\u00a0\u00a0\u00a0 &lt;renderer&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;component-family&gt;javax.faces.Output&lt;\/component-family&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;renderer-type&gt;com.ibm.xsp.PassThroughTagRenderer&lt;\/renderer-type&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;renderer-class&gt;ch.hasselba.xpages.PassThroughTagRendererEx&lt;\/renderer-class&gt;\r\n\u00a0\u00a0\u00a0 &lt;\/renderer&gt;\r\n\u00a0 &lt;\/render-kit&gt;\r\n&lt;\/faces-config&gt;<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>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.\u00a0 A single empty attribute is not allowed, because this would &hellip; <a href=\"https:\/\/hasselba.ch\/blog\/?p=2049\">Weiterlesen <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[46,74],"tags":[7,96,31,3],"class_list":["post-2049","post","type-post","status-publish","format-standard","hentry","category-html5","category-xpages","tag-domino","tag-html5","tag-java","tag-xpages"],"_links":{"self":[{"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/posts\/2049","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2049"}],"version-history":[{"count":7,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/posts\/2049\/revisions"}],"predecessor-version":[{"id":2056,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/posts\/2049\/revisions\/2056"}],"wp:attachment":[{"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2049"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2049"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2049"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}