Since components are rendered as a part of a page, component rendering includes the same steps (see Page rendering):
- before render step. At this step all visible components on a page will receive beforeRender() message and will call onBeforeRender(). A component receives this message from its parent and if the component is MarkupContainer it will send beforeRender() to its immediate children (see MarkupContainer#onBeforeRenderChildren() method).
- rendering. At this step component actually generates markup.
- after render step. This step is similar to the before render step except for the fact that components call onAfterRender() method and that this happens for all visible and invisible components.
Component rendering consists of the following actions:
- all behaviors of this component are notified that component is about to render, which means IBehavior#beforeRender() method is called.
- if there is a IComponentBorder, it is notified that component is about to render (IComponentBorder has nothing to do with Border component though idea is similar)
- onRender() method is called. This method is abstract and is implemented in Component subclasses to perform rendering.
- if there is a IComponentBorder, it is notified that component has been rendered
- all behaviors of this component are notified that component has been rendered, which IBehavior#afterRender() method is called.
Since all the above actions happen during page render step, IBehavior and IComponentBorder before render and after render methods work differently from Component#onBeforeRender() and onAfterRender() methods. The difference is that for IBehavior and IComponentBorder:
- before render methods should not change component state and model
- after render methods will not be called if the component is not visible
(IBehavior has much more capabilities that just onBeforeRender() and onAfterRender() methods, see IBehavior interface for more details).
Actual rendering process is driven by markup (see page rendering). There is one instance of MarkupStream class per page. It contains tags for all markup on the page and keeps track of the current tag in the markup (see #Markup stream). This instance of MarkupStream is passed to onRender() method for every component. Then a component is responsible to advance markup stream to the following tag. There is no restrictions on how many tags a component have to advance markup stream, so component can process as many tags as it wants. Usually component should advance markup stream to the tag next to its closing tag.
On the whole Rendering results in component writing something to the instance of Response class (usually this instance wraps HttpServletResponse). It may look like this:
If component doesn't write anything to the response, nothing from initial markup will be shown on the resulting page.
The Component#onRender() method is an "entry point" for rendering and all components implement it. In this method component must advance markup and probably create some output. Though there can be any logic in onRender() there is a common implementation of it. This implementation is used both in MarkupContainer and WebComponent and there is not a lot of classes that override it. This implementation simply calls final Component#renderComponent() method.
So for the most components renderComponent() method is called. In a simplified way it does the following:
- gets current tag from markup stream. This is the opening tag that corresponds to the current component.
- calls overridable Component#onComponentTag() passing the tag into it
- advances markup stream for component opening tag
- calls overridable Component#onComponentTagBody() passing markup stream and the tag into it
- advances markup stream for the closing tag if there is one
Note that renderComponent() takes part of the responsibility for advancing markup stream for component tags and if a component use this method it only needs to advance markup for the data between its tags. If a component corresponds to an open-close tag like <span/>, then it does not have to do anything.
The onComponentTag() method is designed to read/change tag of a component. The tag is passed as instance of ComponentTag class and then it can be modified. Here are some things that can be done with it:
For more realistic example see Link#onComponentTag() implementation.
onComponentTag() is usually the place where a component check if it is used with correct tag using Component#checkComponentTag() and Component#checkComponentTagAttribute().
onComponentTagBody() is designed to read markup from component tag body and to produce output markup. This is also a method where child components should be handled. As parameters this method gets:
- markup stream which is positioned at the tag next to the component's opening tag. Note that it is the same markup instance that page obtained from IMarkupCache and it is used by all component on the page.
- the tag corresponding to this component which is the same tag as in onComponentTag() method. By this time opening tag is already written to Response and there is no point in modifying it now.
WebComponent subclasses (they can not have child components) in onComponentTagBody() implementation usually just write some markup to the Response instance. There is a convenience method for this Component#replaceComponentTagBody(MarkupStream markupStream, ComponentTag tag, CharSequence body) which replaces component's tag body from markup file with the char sequence and advances markup stream.
MarkupContainer subclasses can do the same things in onComponentTagBody() as WebComponent but since they can have child components, they are responsible to initiate rendering for children. For this there is a convenience method MarkupContainer#renderComponentTagBody() (see also MarkupContainer#renderAll() method that is used by page here). Basically it calls MarkupContainer#renderNext() method until markup stream reaches closing tag for this parent component. The renderNext() method does exactly the same things as it does in case of rendering a page (! add link), namely it find component corresponding to the current tag and asks this component to render.