Skip to main content
cngx-src documentation

CngxRecyclerPlaceholder

DirectivePrimaryv0.1.0WCAG AA

projects/common/data/recycler/recycler-placeholder.directive.ts

Import#

import { CngxRecyclerPlaceholder } from '@cngx/common/data'

Description#

Paints a row-aligned skeleton background under a virtualized scroll region so a fast fling or teleport jump reveals placeholder bars instead of the bare panel/page surface for the single frame the rendered rows lag scrollTop.

The mechanism is a static CSS layer, not a reactive template: by the time a @for over "uncovered indices" could render, change detection has already produced the real rows, so the lag frame is never painted reactively. A background on the offset element always paints and also covers teleport jumps no reactive window can, at zero per-frame cost.

Host it on the offset spacer element(s) - the empty divs (or presentation <li>s) whose height reserves offsetBefore / offsetAfter - one directive instance per spacer. Do NOT host it on a padding-blocked content element that also contains the rows: a background paints across the whole padding box, so it would show through behind every rendered row, not just in the gap. The spacers carry no rows, so the layer is only ever visible where the window has not caught up.

The repeat interval keys off a single representative row height: bind the recycler to read its rowSizeHint, or pass an explicit [rowHeight] fallback so the directive is usable standalone. Visuals are driven by --cngx-recycler-placeholder-* CSS custom properties (see @cngx/common/theming/components/cngx-recycler-placeholder.css); the shimmer drops under prefers-reduced-motion via a spec-observable host class.

One instance per offset spacer, keyed off the recycler's rhythm

<ul>
  @if (recycler.offsetBefore(); as before) {
    <li role="presentation" aria-hidden="true"
        [cngxRecyclerPlaceholder]="recycler" [style.height.px]="before"></li>
  }
  @for (item of visibleItems(); track item.id) { <li>...</li> }
  @if (recycler.offsetAfter(); as after) {
    <li role="presentation" aria-hidden="true"
        [cngxRecyclerPlaceholder]="recycler" [style.height.px]="after"></li>
  }
</ul>

Standalone with an explicit row height

<div cngxRecyclerPlaceholder [rowHeight]="48"
     [style.height.px]="recycler.offsetBefore()"></div>

https://cngxjs.github.io/cngx/examples/#/common/data/recycler/fast-fling-placeholders

Metadata#

Host#

Index#

Inputs#

input()

The recycler whose rowSizeHint sets the placeholder repeat rhythm. Optional: the directive also works from an explicit [rowHeight], so this follows the bridge-input shape (optional + empty-string transform) rather than input.required - a bare cngxRecyclerPlaceholder attribute paired with [rowHeight] is a valid, unbound usage.

default
undefined, {    alias: 'cngxRecyclerPlaceholder',    transform: (value) => (typeof value === 'string' ? undefined : value),  }
rowHeight#number | undefined
input()

Explicit row height (px) used when no recycler is bound, or as a fallback.

HostBindings#

BindingExpression
[class.cngx-recycler-placeholder--shimmer]showShimmer()
[style.--cngx-recycler-placeholder-row-height]rowHeightVar()