Data Views
Generated Markdown for references/concept_data_views.md.
Open book page Back to the skill graph
# Data Views
_Render content from WCL data: wdoc_component / slot / repeater / instance, body & project, partial & collect._
A \*data view\* renders document content — cards, tables, charts, diagrams — from a WCL **data structure** rather than hand-authored blocks. Declare the data once, then derive every view from it. The primary tool is a **component**: a reusable fragment of ordinary wdoc markup with named **slots**.
## Components
Declare a `wdoc_component` with `wdoc_slot`s and a `wdoc_body` of ordinary markup. Reference slots in any `$"…${slot}…"` interpolated string or as a bare identifier in a field (`class = [status]`). A slot with a `default` is optional. Instantiate the component by its own name.
```wcl
wdoc_component dv_metric {
wdoc_slot label
wdoc_slot value
wdoc_slot status { default = "note" }
wdoc_body {
callout $"${label}" { class = [status] body = $"Currently at **${value}%**" }
}
}
// ... then, anywhere a block is allowed:
dv_metric { label = "CPU" value = 42 status = "warning" }
dv_metric { label = "Memory" value = 88 } // status defaults to "note"
```
Live — each call fills the slots and renders the component's `wdoc_body`:
```wcl
dv_metric {
label = "CPU"
value = 42
status = "warning"
}
dv_metric {
label = "Memory"
value = 88
}
```
> [!WARNING]
> **CPU**
> Currently at **42%**
> [!NOTE]
> **Memory**
> Currently at **88%**
> [!TIP]
> **Interpolating slots**
> Slot values land in text via WCL's `$"…"` interpolated strings — note the `$` prefix. A plain `"…"` string is literal. Bare references in a field (like `class = [status]`) need no prefix.
## Repeating over data
`wdoc_repeater` renders its body once per element of `each`, binding the element to the symbol named by `as`. Combined with a component it stamps one card per data row; with no component its body is just markup with the loop variable in scope. A slot can hold a whole list, and a repeater inside a component body can iterate it.
```wcl
wdoc_repeater { each = inventory as = :h
dv_metric { label = h.name value = h.cpu status = h.status }
}
```
Live — one card per element of the data list, the loop variable filling each slot:
```wcl
wdoc_repeater {
each = dv_demo_metrics
as = :m
dv_metric {
label = m.name
value = m.pct
status = m.sev
}
}
```
> [!WARNING]
> **CPU**
> Currently at **42%**
> [!CAUTION]
> **Memory**
> Currently at **88%**
> [!TIP]
> **Disk**
> Currently at **31%**
## Generating pages and navigation
A `wdoc_repeater` is the single iteration concept at every level. At the **document root**, give it a `page` block and it emits one rendered page per element — the page's interpolated label becomes the route. Inside a `toc` (or a `chapter`), give it a `chapter` block and it emits one navigation entry per element.
```wcl
wdoc_repeater { each = containers as = :c
page $"cont_${c.id}" {
sites = [:docbook]
title = c.name
h1 $"${c.name}"
}
}
site docbook {
default_template = :book
toc {
chapter "Containers" {
wdoc_repeater { each = containers as = :c
chapter $"${c.name}" { page = $"cont_${c.id}" }
}
}
}
}
```
> [!NOTE]
> **Routes must be slug-safe and unique**
> A generated route is its interpolated label, so it must be non-empty, contain only `A-Za-z0-9_-`, and be unique within its site. Build a slug from prose with `to_lower(replace(s, " ", "-"))`.
## Render by reference
A `wdoc_instance` renders the component named by the **value** of its `component` field — so a repeater can emit a \*different\* component per element. The instance's like-named fields fill the target's slots (falling back to each slot's `default`).
```wcl
wdoc_repeater { each = widgets as = :row
// `component` is data, so each element picks its own component.
wdoc_instance { component = row.kind label = row.label value = row.value status = row.status }
}
```
## Content slots (layout wrappers)
A `wdoc_content` block in a component body marks where the instance's \*own\* nested blocks render — so a component can frame arbitrary content.
```wcl
wdoc_component dv_panel {
wdoc_slot title
wdoc_body {
h3 $"${title}"
wdoc_content // the caller's nested blocks render here
}
}
dv_panel { title = "Notes"
p "Anything nested in the instance renders at wdoc_content."
list { li "including lists" li "and more" }
}
```
Live — the component frames the caller's own nested blocks:
```wcl
dv_panel {
title = "Notes"
p "Anything nested in the instance renders at the content slot."
list {
li "including lists" li "and more"
}
}
```
### Notes
Anything nested in the instance renders at the content slot.
- including lists
## Partials (scatter and collect)
A `partial` tags a body of blocks; a `collect` with the same tag gathers every matching partial — across the whole document and its imported files — and renders their bodies, in document order, at the collect site. A partial is **invisible where it's defined** unless you set `show_here = true`. It's the appendix / glossary / collected-sidebars pattern.
```wcl
// Scatter tagged deposits anywhere — different blocks, even imported files:
partial aside { callout "From section one" { body = "A point to collect later." } }
// ... prose, other blocks ...
partial aside { callout "From section two" { body = "Another point." } }
// Gather every `aside` partial here, in document order:
collect aside
```
> [!NOTE]
> **Scope and limits**
> Collection is **document-global**: a `collect` gathers matching partials from the root document and every file pulled in by a top-level `import`. Partials in block-scoped (lazily imported) files aren't reached, and a collected body should avoid `id`s.
## Content fragments on data (body and project)
A `body` attaches a chunk of renderable content to a **data record** as a \*property\* — without that record being a renderable block — and a `project` renders it elsewhere by **reference**. Declare your own block type with a `@child("body")` slot, author the content inside each record, then `project` it from a repeater. Because `body` is `@by_ref`, `from = s.overview` resolves to \*that\* record's fragment, and `${…}` inside the body resolves against the record.
```wcl
@block("server")
type Server {
@inline(0) name: identifier
region: utf8?
@child("body") overview: WdocAddressableBody? // content rides on the record
}
server web01 { region = "us-east"
body { p $"Frontend in ${region}." } // NOT a renderable block here
}
page fleet {
wdoc_repeater { each = servers as = :s
h2 $"${s.name}"
project { from = s.overview } // render THIS record's body
}
}
```
> [!NOTE]
> **Addressing**
> A single `@child("body")` slot is addressed by its slot, so the body needs no name. A body in a `@children("body")` list, or one declared at the document root, is addressed by its `@inline(0)` name. The record carrying a body may be nested (a step inside a tutorial). A `body` never renders where it's declared, only where projected.
## Documenting schema types
The built-in `type_table` component documents a schema type by reflecting it — `type_table { type = Image }` renders a table of the type's properties (name, type, required, description), including inherited fields. Descriptions and visibility are authored on the schema with `@doc("…")` and `@hidden`. `block_reference { type = MyDoc }` walks a document's `@child` / `@children` slots and emits an `h3` plus a `type_table` for each.
## Block reference
A `wdoc_component` block: a reusable fragment of wdoc markup with named slots, instantiated by its own name.
| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | `identifier` | yes | |
#### Child blocks
| Slot | Accepts | Multiple | Description |
| --- | --- | --- | --- |
| `slots` | `wdoc_slot` | yes | |
| `body` | `wdoc_body` | no | |
A `wdoc_slot` inside a component: a named, optionally-defaulted parameter filled at instantiation.
| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | `identifier` | yes | |
| `default` | `utf8` | no | |
A `wdoc_body` inside a component: the markup template that renders, with the slots in scope.
| Property | Type | Required | Description |
| --- | --- | --- | --- |
#### Child blocks
| Slot | Accepts | Multiple | Description |
| --- | --- | --- | --- |
| `children` | `WdocBlock` | yes | |
A `wdoc_content` marker in a component body: where the instance's own nested blocks render, framing arbitrary content.
| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `identifier` | no | |
A `wdoc_repeater` block: renders its body once per element of `each`, binding the element to the symbol named by `as`.
| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `each` | `list<WdocItem>` | yes | |
| `as` | `symbol` | yes | |
| `id` | `identifier` | no | |
#### Child blocks
| Slot | Accepts | Multiple | Description |
| --- | --- | --- | --- |
| `children` | `WdocBlock` | yes | |
| `pages` | `page` | yes | |
| `chapters` | `chapter` | yes | |
| `classes` | `class` | yes | |
| `sheets` | `stylesheet` | yes | |
A `wdoc_instance` block: renders the component named by the value of its `component` field, filling slots from its like-named fields.
| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `component` | `utf8` | yes | |
| `id` | `identifier` | no | |
A `partial` block: tags a body of blocks for later collection — invisible where defined unless `show_here = true`.
| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `tag` | `symbol` | yes | |
| `show_here` | `bool` | no | |
| `id` | `identifier` | no | |
#### Child blocks
| Slot | Accepts | Multiple | Description |
| --- | --- | --- | --- |
| `children` | `WdocBlock` | yes | |
A `collect` block: gathers every matching `partial` across the document and renders their bodies in document order at the collect site.
| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `tag` | `symbol` | yes | |
| `id` | `identifier` | no | |
A `body` block: a chunk of renderable content attached to a data record as a property, rendered elsewhere by `project`.
| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | `identifier` | no | |
#### Child blocks
| Slot | Accepts | Multiple | Description |
| --- | --- | --- | --- |
| `children` | `WdocBlock` | yes | |
A `project` block: renders an addressable `body` by reference (`from = …`), resolving `${…}` against that record.
| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `from` | `utf8` | yes | |
| `id` | `identifier` | no | |
## Examples
### One card per data row
A wdoc_repeater renders its body once per element of `each`, binding the element to the symbol named by `as`. Combined with a component, it stamps one card per row of data.
```wcl
wdoc_repeater { each = inventory as = :h
dv_metric { label = h.name value = h.cpu status = h.status }
}
```
**Expected:** One metric card per inventory entry, each reading its label, value, and status from the data row.
## Related
- [Including sub-sites](../references/concept_includes.md)
- [Connections](../references/concept_connections.md)
[← Back to SKILL.md](../SKILL.md)