Pages are asked to render themselves only by IRequestTargets (see Request targets). So the initial message renderPage() comes from a target instance. Page render initiates render of all its subcomponents and is responsible for calling render related methods. Since page extends MarkupContainer most of its render logic is applicable to any MarkupContainer. Page rendering consists of the following steps:

  • before render step
  • rendering
  • after render step

It worth mentioning that before those steps page restores values of all form components which can be persisted (see FormComponent#supportsPersistence()). These values can be restored from anywhere (see IValuePersister interface). By default they are restored from cookies.

Page rendering


Since page rendering logic is dispersed between several classes, three lifelines are used for a single Page instance which is named "page". A message between these lifelines should be considered as a message to the "page" from itself; the method conforming to this message should be considered as being implemented or overriden by the class of the target lifeline.

Before render step

Before render step is responsible for:

  • calling onBeforeRender() method for every component on the page.
  • notifying all registered instances of IComponentOnBeforeRenderListener (you can register a listener using Application#addComponentOnBeforeRenderListener() method)

Both of these actions will include only those components which are going to be rendered. That means invisible components will be skipped.

Page doesn't go through all its subcomponents itself. As a MarkupContainer page handles only its immediate subcomponents with MarkupContainer#onBeforeRenderChildren() method which iterates components and calls Component#beforeRender() method for each of them. If subcomponent is a MarkupContainer, it does the same thing for all it subcomponents. Thus component tree is traversed.

You can change component state and models during this step.

Rendering

Being a Component page render itself as all other components by overriding Component#onRender() method. During the rendering page does the following:

  • obtains a markup stream (see Markup stream) which contains parsed markup for this page. Note that this instance of markup stream will be passed to all subcomponents on the page.
  • calls overridable configureResponse() method which sets response locale, encoding and etc.
  • calls MarkupContainer#renderAll() method which initiates render of all page subcomponents.

The important thing about rendering is that it is markup driven. It means that components are rendered as corresponding tags appear in the markup stream. When there are no more tags in the markup stream rendering is over. This is how renderAll() method is implemented (note that component rendering code is responsible for advancing markup stream):

protected void renderAll(final MarkupStream markupStream)
{
    while (markupStream.hasMore())
    {
        final int index = markupStream.getCurrentIndex();

        renderNext(markupStream);

        if (index == markupStream.getCurrentIndex())
        {
            markupStream.throwMarkupException("Component ... failed to advance the markup stream");
        }
    }
}

The renderNext() method belongs to MarkupContainer class and it is not used exclusively by pages. This method gets current tag from markup stream and attempts to find component which current markup tag corresponds to. Finding a component includes:

  • attempt to get component by id (value from "wicket:id" attribute) from the current markup container which is in case of rendering a page is page
  • attempt to find among parent markup containers the one that implements IComponentResolver interface and that can handle the current tag. This is used for example by Border component to resolve <wicket:body/> tag. In case of rendering a page there is no parent containers.
  • attempt to find IComponentResolver implementation registered at application level which can handle the current tag. These implementations include instances that handles all built-in wicket tags such as <wicket:extend>, <wicket:message> and others. You can add your implementation of IComponentResolver using Settings#addComponentResolver() method.

If component is found, it is asked to render itself by calling Component#render(MarkupStream) method (see Component rendering). Otherwise exception if thrown with a messages like "Unable to find component with id ... This means that you declared wicket:id ... in your markup, but that you either did not add the component to your page at all, or that the hierarchy does not match".

You should not change component state or change model during this step.

After render

After render step is similar to before render step. It includes:

  • calling onAfterRender() method for every component on the page
  • notifying all registered instances of IComponentOnAfterRenderListener (you can register a listener using Application#addComponentOnAfterRenderListener() method)

Unlike before render step these actions will be applied for all components whether there are visible or not. As a MarkupContainer page calls MarkupContainer#onAfterRenderChildren() and process all of its immediate subcomponents.

You should not change component state or change model during this step.

  • No labels