how to create custom loop jsf component: source code and example

Explanation
We know ui:repeat tag. Generally it loop on inner tags. Real application need more features.
Just few examples: paging, need for "index", different styles for odd item.
This simple yet important features are difficult to implement with "ui:repeat" tag
So instead of ui:repeat, use custom "loop" component.
Pros: every programmer, even not-jsf, can understand it. CSS is transparent: what you see is what you get.
Customization is simple too.
Here is explanation and source code for 'loop' component

New to Java, JSF, Icefaces? Here is some links to start with:
Why and when use ICEFACES?
First steps with jsf and java
icefaces without icefaces

Step 1. Create java class for component
null
package com.alternative.iceext.loop;

import java.io.IOException;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;

import com.icesoft.faces.component.panelseries.PanelSeries;

public class Loop extends PanelSeries {
	public static final String COMPONENT_TYPE = "com.alternative.iceext.loop.Loop";

	public static final String RENDERER_TYPE = "com.alternative.iceext.loop.LoopRenderer";

	public final static String COMPONENT_FAMILY = "com.alternative.iceext.loop";

	public String getFamily() {
		return COMPONENT_FAMILY;
	}

	public String getRendererType() {
		return RENDERER_TYPE;
	}

	String varMod;

	int modValueInt = 2;

	String modValue;

	String rowIndexVar;
	
	
	public void encodeBegin(FacesContext context) throws IOException {
		super.encodeBegin(context);
	}
	public void encodeEnd(FacesContext context) throws IOException {
		super.encodeEnd(context);
	}

	public String getRowIndexVar() {
		if (rowIndexVar == null) {
			rowIndexVar = "rowIndex";
		}
		return rowIndexVar;
	}

	public void setRowIndexVar(String rowIndexVar) {
		this.rowIndexVar = rowIndexVar;
	}

	public String getModValue() {
		return modValue;
	}

	public void setModValue(String modValue) {
		this.modValue = modValue;
		if (modValue != null) {
			try {
				modValueInt = 2;
				modValueInt = Integer.parseInt(modValue);
			} catch (Exception e) {

			}
		}
	}

	public String getVarMod() {
		if (varMod == null) {
			varMod = "rowMod";
		}
		return varMod;
	}

	public void setVarMod(String varMod) {
		this.varMod = varMod;

	}

	public void setRowIndex(int rowIndex) {
		super.setRowIndex(rowIndex);
		int modValue = rowIndex % modValueInt;
		FacesContext.getCurrentInstance().getExternalContext().getRequestMap()
				.put(getVarMod(), modValue);
		FacesContext.getCurrentInstance().getExternalContext().getRequestMap()
				.put(getRowIndexVar(), rowIndex);
	}
	
	public void forceRefresh() {
		savedChildren.clear();
	}

	public void recurseForcedRefresh() { 
		recurseForcedRefresh(this);
	}
	
	private void recurseForcedRefresh(UIComponent comp) { 
	    if (comp instanceof Loop) {
	        ((Loop)comp).forceRefresh(); 
	    }
	    java.util.Iterator kids = comp.getFacetsAndChildren(); 
	    while(kids.hasNext()) {
	    	recurseForcedRefresh((UIComponent)kids.next()); 
	    }
	    	
	} 

}

Step 2.Create java class for renderer
null
package com.alternative.iceext.loop;

import java.io.IOException;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;

import com.icesoft.faces.component.panelseries.PanelSeries;

public class Loop extends PanelSeries {
	public static final String COMPONENT_TYPE = "com.alternative.iceext.loop.Loop";

	public static final String RENDERER_TYPE = "com.alternative.iceext.loop.LoopRenderer";

	public final static String COMPONENT_FAMILY = "com.alternative.iceext.loop";

	public String getFamily() {
		return COMPONENT_FAMILY;
	}

	public String getRendererType() {
		return RENDERER_TYPE;
	}

	String varMod;

	int modValueInt = 2;

	String modValue;

	String rowIndexVar;
	
	
	public void encodeBegin(FacesContext context) throws IOException {
		super.encodeBegin(context);
	}
	public void encodeEnd(FacesContext context) throws IOException {
		super.encodeEnd(context);
	}

	public String getRowIndexVar() {
		if (rowIndexVar == null) {
			rowIndexVar = "rowIndex";
		}
		return rowIndexVar;
	}

	public void setRowIndexVar(String rowIndexVar) {
		this.rowIndexVar = rowIndexVar;
	}

	public String getModValue() {
		return modValue;
	}

	public void setModValue(String modValue) {
		this.modValue = modValue;
		if (modValue != null) {
			try {
				modValueInt = 2;
				modValueInt = Integer.parseInt(modValue);
			} catch (Exception e) {

			}
		}
	}

	public String getVarMod() {
		if (varMod == null) {
			varMod = "rowMod";
		}
		return varMod;
	}

	public void setVarMod(String varMod) {
		this.varMod = varMod;

	}

	public void setRowIndex(int rowIndex) {
		super.setRowIndex(rowIndex);
		int modValue = rowIndex % modValueInt;
		FacesContext.getCurrentInstance().getExternalContext().getRequestMap()
				.put(getVarMod(), modValue);
		FacesContext.getCurrentInstance().getExternalContext().getRequestMap()
				.put(getRowIndexVar(), rowIndex);
	}
	
	public void forceRefresh() {
		savedChildren.clear();
	}

	public void recurseForcedRefresh() { 
		recurseForcedRefresh(this);
	}
	
	private void recurseForcedRefresh(UIComponent comp) { 
	    if (comp instanceof Loop) {
	        ((Loop)comp).forceRefresh(); 
	    }
	    java.util.Iterator kids = comp.getFacetsAndChildren(); 
	    while(kids.hasNext()) {
	    	recurseForcedRefresh((UIComponent)kids.next()); 
	    }
	    	
	} 

}

Step 3. Define components in faces-config.xml
null
<?xml version="1.0" encoding="UTF-8"?>

<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
	version="1.2">
	<application>
		<view-handler>com.icesoft.faces.facelets.D2DFaceletViewHandler</view-handler>
	</application>
	<component>
		<component-type>com.alternative.iceext.loop.Loop</component-type>
		<component-class>com.alternative.iceext.loop.Loop</component-class>
	</component>
	<component>
		<component-type>com.alternative.iceext.tags.Tr</component-type>
		<component-class>com.alternative.iceext.tags.Tr</component-class>
	</component>
	<managed-bean>
		<managed-bean-name>testLoops</managed-bean-name>
		<managed-bean-class>com.alternative.beans.TestLoops</managed-bean-class>
		<managed-bean-scope>session</managed-bean-scope>
	</managed-bean>
	<render-kit>
		<renderer>
			<component-family>com.alternative.iceext.tags</component-family>
			<renderer-type>com.alternative.iceext.tags.TrRenderer</renderer-type>
			<renderer-class>com.alternative.iceext.tags.TrRenderer</renderer-class>
		</renderer>
		<renderer>
			<component-family>com.alternative.iceext.loop</component-family>
			<renderer-type>com.alternative.iceext.loop.LoopRenderer</renderer-type>
			<renderer-class>com.alternative.iceext.loop.LoopRenderer</renderer-class>
		</renderer>
	</render-kit>
</faces-config>

Step 4. Define tag library in META-INF directory in you source code, or place you choose.
null
<?xml version="1.0"?>
<!DOCTYPE taglib PUBLIC
  "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
  "http://java.sun.com/dtd//web-jsptaglibrary_1_2.dtd">

<taglib>

  <tlib-version>1.8.0</tlib-version>
  <jsp-version>1.2</jsp-version>
  <short-name>iceext</short-name>
  <uri>http://alternative.com/iceext/component</uri>
  <display-name>ICEfaces Extention</display-name>

  <tag>
    <name>loop</name>
    <tag-class></tag-class>
    <body-content>empty</body-content>
	<attribute>
		<name>id</name>
		<required>false</required>
		<rtexprvalue>false</rtexprvalue>
	</attribute>
	<attribute>
		<name>value</name>
		<required>false</required>
		<rtexprvalue>false</rtexprvalue>
	</attribute>
	<attribute>
		<name>var</name>
		<required>false</required>
		<rtexprvalue>false</rtexprvalue>
	</attribute>
	<attribute>
		<name>varMod</name>
		<required>false</required>
		<rtexprvalue>false</rtexprvalue>
	</attribute>
	<attribute>
		<name>modValue</name>
		<required>false</required>
		<rtexprvalue>false</rtexprvalue>
	</attribute>
	<attribute>
		<name>rowIndexVar</name>
		<required>false</required>
		<rtexprvalue>false</rtexprvalue>
	</attribute>
  </tag>
  <tag>
    <name>tr</name>
    <tag-class></tag-class>
    <body-content>empty</body-content>
	<attribute>
		<name>id</name>
		<required>false</required>
		<rtexprvalue>false</rtexprvalue>
	</attribute>
	<attribute>
		<name>disabled</name>
		<required>false</required>
		<rtexprvalue>false</rtexprvalue>
	</attribute>
	<attribute>
		<name>rendered</name>
		<required>false</required>
		<rtexprvalue>false</rtexprvalue>
	</attribute>
  </tag>
</taglib>
  

This is simple example how to use it.
fragment from LoopComponentSourceCode.jspx
	<table> 
		<caption>Loop component source code</caption> 
		<tr> 
			<td>Name</td> 
			<td>Phone</td> 
			<td>Index</td> 
		</tr> 
		<iceext:loop value="#{loopComponentSourceCodeBean.rows}" var="row" indexVar="rowIndexVar" > 
			<tr> 
				<td><ice:outputText value="#{row.name}" /></td> 
				<td><ice:outputText value="#{row.phone}" /></td> 
				<td><ice:outputText value="#{index}" /></td> 
			</tr> 
		</iceext:loop> 
	</table> 

This is main entry point. Its referenced by 'loopComponentSourceCodeBean' from faces-config.xml
com.gpost.jsfexamples.loopComponentSourceCode.LoopComponentSourceCodeBean
package com.gpost.jsfexamples.loopComponentSourceCode;

import java.util.ArrayList;
import java.util.List;

import com.gpost.jsfexamples.tableWithDeleteOnClick.ManWithPhone;

/*
* Java class for example 'Loop component source code'
* referenced as 'loopComponentSourceCodeBean' from faces-config.xml, 
* used in 'LoopComponentSourceCode.jspx'
*/
public class LoopComponentSourceCodeBean{

	List<ManWithPhone> rows = null;
	
	public List<ManWithPhone> getRows() {
		if (rows == null) {
			rows = new ArrayList<ManWithPhone>();
			// create same sample data...
			rows.add(new ManWithPhone("Gary", "1234-6767"));
			rows.add(new ManWithPhone("Bob", "5678-4553"));
			rows.add(new ManWithPhone("John", "5698-4333"));
		}
		return rows;
	}



} // end of class

This class defines data in 'row'.
com.gpost.jsfexamples.loopComponentSourceCode.ManWithPhone
package com.gpost.jsfexamples.loopComponentSourceCode;

public class ManWithPhone {
	public ManWithPhone(String name, String phone) {
		super();
		this.name = name;
		this.phone = phone;
	}

	String name;
	
	String phone;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getPhone() {
		return phone;
	}

	public void setPhone(String phone) {
		this.phone = phone;
	}
}


part of faces-config.xml
<managed-bean>
 <managed-bean-name>loopComponentSourceCodeBean</managed-bean-name>
 <managed-bean-class>com.gpost.jsfexamples.loopComponentSourceCode.LoopComponentSourceCodeBean</managed-bean-class>
 <managed-bean-scope>session</managed-bean-scope>

</managed-bean>

conclusion
As we saw in this example, you can create custom loop component and then use it with simple html/css, then add some binding to your java beans and you get very flexible, easily customizable layout with more features like paging, indexing, and mod variables.

Getting started with custom loop component

Explanation


New to Java, JSF, Icefaces? Here is some links to start with:
Why and when use ICEFACES?
First steps with jsf and java
icefaces without icefaces

We know ui:repeat tag. Generally it loop on inner tags. Real application need more features.
Just few examples: paging, need for "index", different styles for odd item.
This simple yet important features are difficult to implement with "ui:repeat" tag
So instead of ui:repeat, use custom "loop" component.
Pros: every programmer, even not-jsf, can understand it. CSS is transparent: what you see is what you get.
Customization is simple too.
Use you favorite HTML editor, or JSF editor together with CSS tools to customize every aspect of this example, its easy!
fragment from LoopGettingStarted.jspx
	<table> 
		<caption>Loop getting started</caption> 
		<tr> 
			<td>Name</td> 
			<td>Phone</td> 
			<td>Index</td> 
		</tr> 
		<iceext:loop value="#{loopGettingStartedBean.rows}" var="row" rowIndexVar="rowIndexVar" > 
			<tr> 
				<td><ice:outputText value="#{row.name}" /></td> 
				<td><ice:outputText value="#{row.phone}" /></td> 
				<td><ice:outputText value="#{rowIndexVar}" /></td> 
			</tr> 
		</iceext:loop> 
	</table> 

This is main entry point. Its referenced by 'loopGettingStartedBean' from faces-config.xml
com.gpost.jsfexamples.loopGettingStarted.LoopGettingStartedBean
package com.gpost.jsfexamples.loopGettingStarted;

import java.util.ArrayList;
import java.util.List;



/*
* Java class for example 'Loop getting started'
* referenced as 'loopGettingStartedBean' from faces-config.xml, 
* used in 'LoopGettingStarted.jspx'
*/
public class LoopGettingStartedBean{

	List<ManWithPhone> rows = null;
	
	public List<ManWithPhone> getRows() {
		if (rows == null) {
			rows = new ArrayList<ManWithPhone>();
			// create same sample data...
			rows.add(new ManWithPhone("Gary", "1234-6767"));
			rows.add(new ManWithPhone("Bob", "5678-4553"));
			rows.add(new ManWithPhone("John", "5698-4333"));
		}
		return rows;
	}



} // end of class

This class defines data in 'row'.
com.gpost.jsfexamples.loopGettingStarted.ManWithPhone
package com.gpost.jsfexamples.loopGettingStarted;

public class ManWithPhone {
	public ManWithPhone(String name, String phone) {
		super();
		this.name = name;
		this.phone = phone;
	}

	String name;
	
	String phone;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getPhone() {
		return phone;
	}

	public void setPhone(String phone) {
		this.phone = phone;
	}
}


part of faces-config.xml
<managed-bean>
 <managed-bean-name>loopGettingStartedBean</managed-bean-name>
 <managed-bean-class>com.gpost.jsfexamples.loopGettingStarted.LoopGettingStartedBean</managed-bean-class>
 <managed-bean-scope>session</managed-bean-scope>

</managed-bean>

conclusion
As we saw in this example, you can use loop with simple html/css, then add some binding to your java beans and you get very flexible, easily customizable layout with more features like paging, indexing, and mod variables.

Example for custom loop jsf component with zebra style and index

Explanation


New to Java, JSF, Icefaces? Here is some links to start with:
Why and when use ICEFACES?
First steps with jsf and java
icefaces without icefaces

Table and other controls are better when inner components displayed in different styles, on 'mod' style or 'zebra style' Lets see how to do it without complexity, compact and easy way.
This simple yet important features are difficult to implement with "ui:repeat" tag
Instead of ui:repeat, use custom "loop" component.
Pros: every programmer, even not-jsf, can understand it. CSS is transparent: what you see is what you get.
Customization is simple too.
Use you favorite HTML editor, or JSF editor together with CSS tools to customize every aspect of this example, its easy!
fragment from LoopWithZebraStyleAndIndex.jspx
	 <style> 
	 .zebraStyle0 { 
	 	background-color: gray; 
	 } 
	 .zebraStyle1 { 
	 	background-color: blue; 
	 } 
	 .zebraStyle2 { 
	 	background-color: navy; 
	 } 
	 </style> 
	<table> 
		<caption>Loop with zebra style and index</caption> 
		<tr> 
			<td>Name</td> 
			<td>Phone</td> 
			<td>Index</td> 
		</tr> 
		<iceext:loop value="#{loopWithZebraStyleAndIndexBean.rows}" var="row" rowIndexVar="rowIndexVar" varMod="mod3" modValue="3"> 
			<tr class="zebraStyle#{mod3}"> 
				<td><ice:outputText value="#{row.name}" /></td> 
				<td><ice:outputText value="#{row.phone}" /></td> 
				<td><ice:outputText value="#{rowIndexVar}" /></td> 
			</tr> 
		</iceext:loop> 
	</table> 

This is main entry point. Its referenced by 'loopWithZebraStyleAndIndexBean' from faces-config.xml
com.gpost.jsfexamples.loopWithZebraStyleAndIndex.LoopWithZebraStyleAndIndexBean
package com.gpost.jsfexamples.loopWithZebraStyleAndIndex;

import java.util.ArrayList;
import java.util.List;


/*
* Java class for example 'Loop with zebra style and index'
* referenced as 'loopWithZebraStyleAndIndexBean' from faces-config.xml, 
* used in 'LoopWithZebraStyleAndIndex.jspx'
*/
public class LoopWithZebraStyleAndIndexBean{

	List<ManWithPhone> rows = null;
	
	public List<ManWithPhone> getRows() {
		if (rows == null) {
			rows = new ArrayList<ManWithPhone>();
			// create same sample data...
			rows.add(new ManWithPhone("Gary", "1234-6767"));
			rows.add(new ManWithPhone("Bob", "5678-4553"));
			rows.add(new ManWithPhone("John", "5698-4333"));
			rows.add(new ManWithPhone("John1", "5698-4333"));
			rows.add(new ManWithPhone("John2", "5698-4333"));
			rows.add(new ManWithPhone("John3", "5698-4333"));
			rows.add(new ManWithPhone("John4", "5698-4333"));
		}
		return rows;
	}



} // end of class

This class defines data in 'row'.
com.gpost.jsfexamples.loopWithZebraStyleAndIndex.ManWithPhone
package com.gpost.jsfexamples.loopWithZebraStyleAndIndex;

public class ManWithPhone {
	public ManWithPhone(String name, String phone) {
		super();
		this.name = name;
		this.phone = phone;
	}

	String name;
	
	String phone;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getPhone() {
		return phone;
	}

	public void setPhone(String phone) {
		this.phone = phone;
	}
}


part of faces-config.xml
<managed-bean>
 <managed-bean-name>loopWithZebraStyleAndIndexBean</managed-bean-name>
 <managed-bean-class>com.gpost.jsfexamples.loopWithZebraStyleAndIndex.LoopWithZebraStyleAndIndexBean</managed-bean-class>
 <managed-bean-scope>session</managed-bean-scope>

</managed-bean>

conclusion
As we saw in this example, you can use loop with simple html/css, then add some binding to your java beans and you get very flexible, easily customizable layout with more features like paging, indexing, and mod variables.