Iterative custom TCDL tag example
The following example illustrates how to create a custom TCDL tag called tcdl:ForEachCar that iterates over an array of cars defined in your Java class.
On your webpage, the TCDL tag would appear in the following kind of context:
<html>
<body>
<tcdl:ForEachCar>
<tcdl:CarName />
</tcdl:ForEachCar>
</body>
</html>
The code that processes the tcdl:ForEachCar tag is as follows:
package com.tridion.tcdl.iterationtagrenderers;
import com.tridion.tcdl.*;
import java.util.ArrayList;
import java.util.List;
public class ForEachCarRenderer implements TagRenderer, IterationTagSupport {
private final String currentCarPropertyName = "Foreach:CurrentCar";
private final String carsPropertyName = "Foreach:Cars";
private int currentIterationIndex = 0;
@Override
public boolean requiresCodeBlock(TransformContext context, OutputDocument target, Tag tag)
{
return false;
}
@Override
public int doStartTag(Tag tag, StringBuffer tagBody, TransformContext context, OutputDocument target)
throws TCDLTransformerException
{
// For the sake of simplicity, we initialize the context with a list of cars in the
// doStartTag() method of the iteration tag.
initTransformContext(context);
// We set the current car property so that the enclosed CarName tag can process it.
setCurrentContextForIteration(context);
List<String> carNames = context.getProperty(getCarsPropertyName(), new ArrayList<String>());
tagBody.append(String.format("<h1>Available: %d Cars</h1>", carNames.size()));
tagBody.append("\n");
tagBody.append(String.format("<!-- Starting %s FOREACH -->", "Car"));
return Tag.CONTINUE_TAG_EVALUATION;
}
@Override
public int doAfterBody(Tag tag, StringBuffer tagBody, TransformContext context, OutputDocument target)
throws TCDLTransformerException
{
List<String> carNames = context.getProperty(getCarsPropertyName(), new ArrayList<String>());
if (currentIterationIndex < carNames.size()) {
// There are still cars left to process.
setCurrentContextForIteration(carNames, context);
return IterationTagSupport.EVAL_BODY_AGAIN;
}
// There are no more cars left to process.
return IterationTagSupport.SKIP_BODY;
}
@Override
public String doEndTag(Tag tag, StringBuffer tagBody, TransformContext context, OutputDocument target)
throws TCDLTransformerException
{
tagBody.append("\n");
tagBody.append(String.format("<!-- Finishing %s FOREACH -->", "Car"));
return tagBody.toString();
}
public String getCurrentCarPropertyName() {
return currentCarPropertyName;
}
// One iteration: set the context, increase the iterator.
private void setCurrentContextForIteration(List<String> carNames, TransformContext context)
{
if (currentIterationIndex < carNames.size()) {
// We set the current car property in TransformContext
// This TransformContext is available to all enclosed tags of the ForEachCar tag.
context.setProperty(getCurrentCarPropertyName(), carNames.get(currentIterationIndex));
currentIterationIndex++;
}
}
private void setCurrentContextForIteration(TransformContext context)
{
List<String> carNames = context.getProperty(getCarsPropertyName(), new ArrayList<String>());
setCurrentContextForIteration(carNames, context);
}
private void initTransformContext(TransformContext localContext)
{
List<String> cars = new ArrayList<String>();
cars.add("Audi");
cars.add("BMW");
cars.add("Fiat");
cars.add("Mazda");
localContext.setProperty(getCarsPropertyName(), cars);
}
private String getCarsPropertyName()
{
return carsPropertyName;
}
}
The code that processes the tcdl:CarName tag is as follows:
package com.tridion.tcdl.iterationtagrenderers;
import com.tridion.tcdl.OutputDocument;
import com.tridion.tcdl.TCDLTransformerException;
import com.tridion.tcdl.Tag;
import com.tridion.tcdl.TagRenderer;
import com.tridion.tcdl.TransformContext;
public class CarNameRenderer implements TagRenderer {
@Override
public int doStartTag(Tag tag, StringBuffer tagBody, TransformContext context, OutputDocument target)
throws TCDLTransformerException
{
return Tag.CONTINUE_TAG_EVALUATION;
}
@Override
public String doEndTag(Tag tag, StringBuffer tagBody, TransformContext context, OutputDocument target)
throws TCDLTransformerException
{
// This example purposely simplifies the code for demonstration purposes:
// It performs no exception handling, when you actual class obviously should do so.
// We retrieve the handler of the enclosing tag to get context information.
ForEachCarRenderer forEachRenderer = (ForEachCarRenderer)tag.getEnclosingTag().getHandler();
final String currentCarPropertyName = forEachRenderer.getCurrentCarPropertyName();
// We use TransformContext to access all properties saved in the parent's context.
final String currentCarName= context.getProperty(currentCarPropertyName, "");
tagBody.append(String.format("<div>%s</div>", currentCarName));
return tagBody.toString();
}
@Override
public boolean requiresCodeBlock(TransformContext context, OutputDocument target, Tag tag)
{
return false;
}
}
The tag bundle XML file for the tags is as follows:
<TagRegistry>
<Tags>
<Tag Namespace="tcdl" Name="CarName">
<Handler Class="com.tridion.tcdl.iterationtagrenderers.CarNameRenderer" AllowCodeBlock="true" />
</Tag>
<Tag Namespace="tcdl" Name="ForEachCar">
<Handler Class="com.tridion.tcdl.iterationtagrenderers.ForEachCarRenderer" AllowCodeBlock="true" />
</Tag>
</Tags>
</TagRegistry>
The expected output for the HTML input would be as follows:
<html>
<body>
<h1>Available: 4 Cars</h1>
<!-- Starting Car FOREACH -->
<div>Audi</div>
<div>BMW</div>
<div>Fiat</div>
<div>Mazda</div>
<!-- Finishing Car FOREACH -->
</body>
</html>