Row Pinning
Two kinds of band that never scroll out of the way. gcPinnedTop and gcPinnedBottom project your own markup across the full width; subHeaderTemplate and footerTemplate put a cell under each column instead. Which you want comes down to one question — does the content line up with a column, or not?
Full width — gcPinnedTop and gcPinnedBottom
Your own markup, projected into the grid and spanning it end to end. Scroll the columns sideways: the banner and the status bar do not move with them, because they belong to the grid rather than to any column.
Put an element inside <gridcraft>, add gcPinnedTop or
gcPinnedBottom, and that is the whole API:
<gridcraft […]>
<div gcPinnedTop class="banner">Q2 invoice summary</div>
<div gcPinnedBottom class="status-bar">10 invoices</div>
</gridcraft>
It is your markup, with your bindings, your classes and your components inside it — not a config object the grid renders for you. The two directives only decide where it goes.
They span the whole grid and do not scroll with the columns. There are nine columns here and the card is narrower than they are, so the body scrolls sideways — and the banner and the status bar stay exactly where they are. That is the difference from the second example, and it is only visible on a grid that overflows.
They also sit outside the row area, so paging, virtual scrolling and row height
leave them alone. The numbers come from (rowsLoaded) — the rows the grid is holding —
through ordinary computed()s, which is why they cannot drift from what is on screen.
Column aligned — subHeaderTemplate and footerTemplate
The same numbers, put under the columns they belong to. Scroll sideways and these bands move with the columns, because each one is a cell rather than a strip.
The same numbers, in cells instead of a strip. subHeaderTemplate renders between the
header and the first row; footerTemplate renders under the last one. Both are
declared per column, so each band gets a cell exactly as wide as the column it belongs to:
{ key: 'amount', name: 'Amount', prop: 'amount',
subHeaderTemplate: subAmountTpl(),
footerTemplate: footerAmountTpl() }
Scroll sideways and these bands move with the columns — Avg stays under Amount, the paid percentage stays under Status. A full-width band cannot do that, and a column-aligned band cannot hold a toolbar. That is the whole decision.
Declaring either template on any column turns its band on for the whole grid; the columns that declare nothing render an empty cell in it. Both bands stay put while the rows scroll, exactly as the header does.
The template mechanics — the contexts, and getting a TemplateRef onto a column
without breaking server-side rendering — are on
Custom Cell Templates.
Two bands, one question
Both of these stay on screen while the rows scroll. They differ in what they are made of, and the choice is whether the content belongs to a column or to the grid:
gcPinnedTop/gcPinnedBottom— your own element, projected into the grid, spanning it end to end. It does not scroll horizontally with the columns. Banners, bulk-action toolbars, status bars, alerts.subHeaderTemplate/footerTemplate— one cell per column, aligned to the column boundaries and scrolling with them. Per-column totals, averages, units, range hints.
Nothing stops you using both on one grid — a toolbar across the top and totals under the columns is a common pairing. They are separated here so the horizontal-scroll difference is visible; on a grid that fits its container the two look alike and the choice looks arbitrary.
Where the numbers come from
Neither band computes anything. (rowsLoaded) emits the rows the grid is holding, and
everything on screen is derived from that with ordinary computed()s — so a sort, a
filter or a page change moves the totals with the rows, and a band can never disagree with the
body above it.
A total over the whole data set rather than the loaded page is your call: keep it in the same signal and fill it from your API's response. The grid does not know your aggregate, and does not pretend to.
gcPinnedTop and gcPinnedBottom are directives on projected
elements, not config properties. Import them alongside GridcraftComponent
— a missing import is a silent no-op, because an unknown attribute on a div is
just an attribute.
Pinned rows API
| Property | Type | Default | Description |
|---|---|---|---|
| gcPinnedTop | directive (projected element) | — | A full-width element fixed above the column header. Does not scroll horizontally with the columns, and sits outside the row area. |
| gcPinnedBottom | directive (projected element) | — | The same, below the scroll area — status bars, bulk-action toolbars, running totals. |
| column.subHeaderTemplate | TemplateRef<SummaryContext<T>> | — | A column-aligned band between the header and the first row. Declaring it on one column turns the band on for the whole grid. |
| column.footerTemplate | TemplateRef<SummaryContext<T>> | — | A column-aligned band under the last row. Same context, same whole-grid rule. See Custom Cell Templates. |
| (rowsLoaded) | EventEmitter<T[]> | — | The rows the grid is currently holding. Both examples derive every figure they show from it, so the bands cannot drift from the body. |