Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: restore

Rendering of components in Tapestry 5 is based on a state machine and a queue (instead of the tail recursion used in Tapestry 4). This breaks the rendering process up into tiny pieces that can easily be implemented or overridden. Don't worry, in practice, writing components requires a breathtakingly small amount of code.

Div
stylefloat:right
titleRelated Articles
classaui-label
Content by Label
showLabelsfalse
showSpacefalse
titleRelated Articles
cqllabel in ("request-processing","rendering") and space = currentSpace()

Rendering Phases

The rendering of each component is divided into a number of phases, illustrated below.

Image AddedImage Removed
Each of the orange phases (SetupRender, BeginRender, BeforeRenderBody, etc.) corresponds to an annotation you may place on one or more methods of your class. The annotation directs Tapestry to invoke your method as part of that phase.

...

Render phase methods may take no parameters, or may take a parameter of type MarkupWriter. The methods can have any visibility you like ... typically, package private is used, as this visibility makes it possible to unit test your code (from within the same Java package) without making the methods part of the component's public API.

...

The large number of phases reflects the need for precise control of components from component mixins. Several of the phases exist almost exclusively for mixins.

...

Here's the source for a looping component that counts up or down between two values, renders its body a number of times, and stores the current index value in a parameter:

Code Block
java
java
package org.example.app.components;

import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.annotations.AfterRender;
import org.apache.tapestry5.annotations.SetupRender;

public class Count
{
    @Parameter
    private int start = 1;

    @Parameter(required = true)
    private int end;

    @Parameter
    private int value;

    private boolean increment;

    @SetupRender
    void initializeValue()

...


    {
        value = start;

        increment = start < end;
    }

    @AfterRender
    boolean next()
    {
        if (increment)
        {
            int newValue = value + 1;

            if (newValue <= end)
            {
                value = newValue;
                return false;
            }
        }
        else
        {
            int newValue = value - 1;

            if (newValue >= end)
            {
                value = newValue;
                return false; 
            }
        }

        return true;
    }
}

Returning false from next() causes Tapestry to re-run the BeginRender phase, and from there, re-render the component's body (this component does not have a template). Returning true transitions to the CleanupRender phase.

...

What's really mind blowing is that the template and body of a component will often contain ... more components! That means that many different components will be in different phases of their own state machine.

Render Phases in Detail

Wiki Markup
{float:right|background=#eee|width=50%}
{note}
The SetupRender phase, like all render phases, occurs once for each rendering of the component. If the component is inside a looping component ([Loop|http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/Loop.html], [Grid|http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/Grid.html], etc.), then the SetupRender method will be called once for _each_ iteration of the loop.
{note}
{float}

SetupRender

The SetupRender phase (see @SetupRender) is where you can perform any one-time per-render setup for your component. This is a good place to read component parameters and use them to set temporary instance variables.

...

Using this mechanism, the earlier example can be rewritten as:

...

Code Block
java
java
package org.example.app.components;

import org.apache.tapestry5.annotations.Parameter;

public class Count
{
    @Parameter
    private int start = 1;

    @Parameter(required = true)
    private int end;

    @Parameter
    private int value;

...



    private boolean increment;

    void setupRender()
    {
        value = start;

        increment = start < end;
    }

    boolean afterRender()
    {
        if (increment)
        {
            int newValue = value + 1;

            if (newValue <= end)
            {
                value = newValue;
                return

...

 false;
            }
        }
        else
        {
            int newValue = value - 1;

            if (newValue >= end)
            {
                value = newValue;
                return false; 
            }
        }

        return true;
    }
}

This style is a trade off: on the gain side, the code is even simpler and shorter, and the method names will, by design, be more consistent from one class to the next. The down side is that the names are very generic, and may in some cases, be less descriptive than using annotated methods (initializeValue() and next() are, to some eyes, more descriptive).

...

The following component returns a Renderable in the BeginRender phase and skips the BeforeRenderTemplate phase:

Code Block
langjava
public class OutputValueComponent
{
    @Parameter
    private String

...

 value;
    
    Object beginRender()
    {
        return new Renderable()
        {
            public void render(MarkupWriter

...

 writer)
            {
                writer.write(value);
            }
        };
    }
}

Short Circuiting

If a method returns a true or false value, this will short circuit processing. Other methods within the phase that would ordinarily be invoked will not be invoked.

...

Mixins Before Component

When a component has mixins, then the mixins' render phase methods execute before the component's render phase methods. If a mixin extends from a base class, the mixin's parent class methods execute before the mixin subclass' render phase methods.

...

The order in which the mixins of a given class (@MixinAfter or mixins before) execute is determined by the ordering constraints specified for the mixins. If no constraints are provided, the order is undefined. See Component MixinsRendering for more details.

Parents before Child

...

Rendering comments are only available when not running in production mode.

To turn on rendering comments for all requests, set the tapestry.component-render-tracing-enabled configuration symbol to "true".

To turn on rendering comments only for a particular request, add the query parameter t:component-trace=true to the URL:

Code Block
  http://www.example.com/myapp/mypage?t:component-trace=true