Assigning custom tag name to repeater

Generally you use a repeater to house table rows or list elements. It turns out though, that you aren’t given the possibility to define which tag output for it. In order to work around that I have seen solutions like this:

<xp:repeat id="repeat1" rows="30" value="#{view}" var="row">
     <xp:this.facets>
	 	<xp:text disableTheme="true" xp:key="header" escape="false">
			<xp:this.value><![CDATA[<ul>]]></xp:this.value>
        </xp:text>

        <xp:text disableTheme="true" xp:key="footer" escape="false">
        	<xp:this.value><![CDATA[</ul>]]></xp:this.value>
        </xp:text>
     </xp:this.facets>                
             
     <li>
	 	<xp:text value="#{row.whatever}"/>
     </li>
</xp:repeat>

Yes, the result is somewhat achieved but, to begin with, that is surely painful code to look at. Secondly, I say somewhat because the very presence of the repeater id repeat1 makes the component output a wrapping div (unless you explicitly set disableOutputTag="true").
Actually that could be what you need if, say, you wanted to partial refresh the repeater block only (you need a clientId for the AJAX to make hold of)? If you don’t then expect to see this kind of error:

ajax error

But what if you want to output it in a tbody? From a HTML syntax standpoint the enclosing div would become an issue. At that point the eventual workaround would be to remove the repeater id or disable the output tag – thus preventing the repeater from outputting the outer div – and partial refresh the table.Yay, more cranky code…

Anyway, in the process of building my own custom renderers, while peeping into the IBM decompiled code I came across this repeat renderer (IteratorRenderer) piece of code:

private Renderer getDelegate(FacesContext paramFacesContext)
/*     */   {
/*  77 */     Renderer localRenderer = paramFacesContext.getRenderKit().getRenderer("com.ibm.xsp.Repeat", "javax.faces.Panel");
/*  78 */     return localRenderer;
/*     */   }
/*     */   
/*     */   public void encodeBegin(FacesContext paramFacesContext, UIComponent paramUIComponent) throws IOException
/*     */   {
/*  83 */     int i = (paramUIComponent instanceof UIDataIterator) ? ((UIDataIterator)paramUIComponent).isRemoveRepeat() : 0;
/*  84 */     if (i == 0) {
/*  85 */       getDelegate(paramFacesContext).encodeBegin(paramFacesContext, paramUIComponent);
/*     */     }
/*     */     
/*     */ 
/*  89 */     if (paramUIComponent.getFacetCount() > 0) {
/*  90 */       UIComponent localUIComponent = paramUIComponent.getFacet("header");
/*  91 */       if (localUIComponent != null) {
/*  92 */         FacesUtil.renderComponent(paramFacesContext, localUIComponent);
/*     */       }
/*     */     }
/*     */   }

So I got to understand that, in order to generate the enclosing container the framework delegates its creation to the regular javax.faces.Panel renderer. That’s was cute discovery news because if it was as I thought I could exploit it by populating the repeat control themeId property in order to infer an arbitrary tag name. How? Why? Say I have this theme definitions:

	<control>
		<name>List.Table</name>
		<property>
			<name>tagName</name>
			<value>tbody</value>
		</property>
	</control>

	<control>
		<name>List.Ordered</name>
		<property>
			<name>tagName</name>
			<value>ol</value>
		</property>
	</control>

	<control>
		<name>List.Unordered</name>
		<property>
			<name>tagName</name>
			<value>ul</value>
		</property>
	</control>

And this code:

<xp:repeat id="repeat2" rows="30" value="#{view}" var="row" themeId="List.Unordered">
     <li>
	 	<xp:text value="#{row.whatever}"/>
     </li>
</xp:repeat>

<table>
     <xp:repeat id="repeat3" rows="30" value="#{view}" var="row" themeId="List.Table">
          <tr><td>
	 	     <xp:text value="#{row.whatever}"/>
          </td></tr>
     </xp:repeat>
</table>

Boom! I cured my eyes and provided for the partial refresh ability with a nifty trick.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.