-
The <o:lazyPanel> is a component that defers rendering of its children until the panel has scrolled into view. This is useful for expensive regions below the fold, where building the children would otherwise happen on every page load even though the user may never scroll far enough to see them.
On initial render, the component writes a wrapper element with a placeholder (optional, see "Placeholder" below) and schedules a viewport intersection listener on it via OmniFaces.js, which uses IntersectionObserver when available and falls back to scroll/resize/orientationChange listeners otherwise. As soon as the wrapper intersects the viewport, a single faces.ajax.request targeting its own client id is fired. During that request, the component detects the trigger, invokes the optional listener with a LazyPanelEvent argument, flips its loaded flag, and renders its children in place of the placeholder.
Usage
<o:lazyPanel>
<h:dataTable value="#{bean.expensiveList}" var="row">
...
</h:dataTable>
</o:lazyPanel>
The loaded attribute is a server-side escape hatch: when true, the children are rendered immediately without any client side observer. This is useful for print views, SEO crawlers, or tests.
Listener
The listener attribute can be used to invoke a bean method with an optional LazyPanelEvent argument. It is invoked exactly once, when the component is triggered by the client side intersection.
<o:lazyPanel listener="#{bean.load}">
...
</o:lazyPanel>
The listener is invoked from decode(FacesContext), which then jumps straight to the render response phase. The component therefore effectively behaves like a UICommand with immediate="true": the process validations, update model values and invoke application phases are skipped for the lazy panel ajax request, so any concurrently submitted input values in the enclosing form are not processed, validated, or applied. If you need those values on the server, submit them through a separate ajax request before the lazy panel triggers.
Request parameters
Nested <f:param> or <o:param> children are sent along with the lazy panel ajax request and can be retrieved by the listener via Faces.getRequestParameter(String). This is useful to pass context (like an entity id, filter key, or page number) so that a single listener can serve multiple panels.
<o:lazyPanel listener="#{bean.load}">
<f:param name="productId" value="#{product.id}" />
...
</o:lazyPanel>
Parameter values are evaluated at initial render (snapshot semantics), consistent with UIParameter usage elsewhere in Faces. For values that must be fresh at trigger time, read them from the surrounding bean state in the listener itself.
Layout
By default the wrapper is rendered as a <div>. Set layout="inline" to render a <span> instead:
<o:lazyPanel layout="inline">
...
</o:lazyPanel>
Placeholder
To show a skeleton or spinner while the lazy region is still off-screen, put it in a facet named placeholder:
<o:lazyPanel>
<f:facet name="placeholder">
<div class="skeleton">Loading...</div>
</f:facet>
...
</o:lazyPanel>
VDL documentation
API documentation
Java source code
org.omnifaces.component.output.LazyPanelHandlerorg.omnifaces.event.LazyPanelEventorg.omnifaces.component.output.LazyPanel