---
title: "Data views: a query shown as table, list or board"
description: "The dataView Block type: its stored query and layout, the three layouts and what a board needs, how the view asks and refreshes, /ansicht, and the Typed blocks table as the table layout."
canonical_url: "https://skaile-ai.github.io/forge-editor/guides/data-views"
---
# Data views: a query shown as table, list or board

> The dataView Block type: its stored query and layout, the three layouts and what a board needs, how the view asks and refreshes, /ansicht, and the Typed blocks table as the table layout.

A **data view** is a [Typed block](https://skaile-ai.github.io/forge-editor/guides/composites) of the Block type `dataView` that shows the answer to a [query](https://skaile-ai.github.io/forge-editor/guides/query) as a table, a list or a board. It stores the query and the layout and nothing else: the rows are never part of the Document, they are asked for whenever the view is on screen. Copy the Document, export it, or let a collaborator open it: the view shows what the query finds at that moment.

`defaultBlockTypes(t)` lists the type (label „Ansicht“ / "View", icon `i-lucide-table-properties`, capabilities `properties` and the colour basket), so a host that spreads the built-in list has it.

## What a data view stores

Two [Block properties](https://skaile-ai.github.io/forge-editor/guides/block-properties) in the Typed block's `meta`:

<table>
<thead>
  <tr>
    <th>
      Key
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Meaning
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        query
      </code>
    </td>
    
    <td>
      <code>
        text
      </code>
      
      , hidden
    </td>
    
    <td>
      a <code>
        DataQuery
      </code>
      
       as JSON text; the properties form never shows it
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        layout
      </code>
    </td>
    
    <td>
      <code>
        select
      </code>
    </td>
    
    <td>
      <code>
        table
      </code>
      
       (the default), <code>
        list
      </code>
      
       or <code>
        board
      </code>
    </td>
  </tr>
</tbody>
</table>

```ts
import { dataViewLayoutOf, dataViewQueryOf, newDataViewMeta } from '@skaile/forge-editor/composites'

newDataViewMeta('task')   // { query: '{"v":1,"source":{"kind":"blocks","blockType":"task"}}' }
dataViewQueryOf(node)     // DataQuery | null; null for no query, bad JSON or a query that names no Block type
dataViewLayoutOf(node)    // 'table' | 'list' | 'board'; an unknown value reads as 'table'
```

`dataViewQueryOf` reads the JSON text like `collectionQueryOf` does and checks only its shape; `validateQuery` names the issues against the Block type's fields, and a view whose query is wrong shows them instead of rows.

## The three layouts

A layout turns the `DataResult` into the model a view draws (`VIEW_LAYOUTS`, `features/composites/views/layouts.ts`). Cell text, sort keys and the right-hand alignment of numbers come from the Property kind registry (`PROPERTY_KINDS`), so a new property type shows in every layout without code here.

<table>
<thead>
  <tr>
    <th>
      Layout
    </th>
    
    <th>
      Shows
    </th>
    
    <th>
      Requires
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        table
      </code>
    </td>
    
    <td>
      one row per Block: title, Block type, then one column per property (long text such as a transcript and hidden properties get none)
    </td>
    
    <td>
      nothing
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        list
      </code>
    </td>
    
    <td>
      the title first, then the short properties as fields
    </td>
    
    <td>
      nothing
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        board
      </code>
    </td>
    
    <td>
      one lane per option of the group field, a card per Block
    </td>
    
    <td>
      <code>
        group
      </code>
    </td>
  </tr>
</tbody>
</table>

A board needs a group in the query: its lanes are the options of the grouped property. For a `status` property the lane carries the option's group (`todo`, `doing`, `done`), for a `select` the options in their declared order; a lane of an option nobody uses stays on the board (a board shows where nothing is yet), the lane of Blocks without a value comes last, and `group.hideEmpty` drops it when it holds nothing. `viewModelOf(layout, result, defs, env, input)` refuses a query without a group for the board (`{ ok: false, missing: ['group'] }`), and the view then says so instead of drawing no lanes.

Choosing the board in the view (`chooseDataViewLayout(editor, node, 'board')`) on a query without a group writes the `status` property (else the first `status` or `select` property of the queried Block types) as the group, so the board shows lanes at once. Going back to the table keeps the group.

## How a view asks

The model is `features/composites/views/data-view-model.ts` (`createDataViewModel`), shared by `DataView.vue` and `DataView.tsx`: one model, two thin twins.

- It answers with `backend.index.query` (the Blocks of every Document the user may read). A backend without it answers the same query locally over the Blocks of the open Document, with `evaluateQuery`; the line below the view then says „Nur die Blöcke dieses Dokuments“.
- The state is one of `no-query`, `loading`, `ready`, `needs-group` and `error` (a refused query lists its `QueryIssue`s). The previous rows stay on screen while the next answer is on its way, and an answer that equals the one shown does not redraw the view.
- Only a view on screen asks. The node view reports its visibility (`model.setVisible`); a visible view listens for changes of the Blocks (its own Document and the Blocks of other Documents, as the reference chips do), asks again 200 ms after the last change (`DATA_VIEW_DEBOUNCE_MS`), and every `FOREIGN_CONTENT_REFRESH_MS` (the Blocks of other Documents change without the editor hearing of it). A view scrolled away stops all three and asks again when it comes back.
- A change of the block's `query` or `layout` asks at once.
- Row clicks reveal the Block in the open Document, or open it in its own Document through the host's `openBlock` (`revealCollectionItem`).

## Node view

Both bindings supply the node view (`CompositeNodeViews.dataView`): the usual Typed block view with the answer below its content, and layout buttons while the editor is editable. A binding that renders no live content leaves `dataView` out of `nodeViews`; the block then renders as any Typed block and its query stays in `meta.query`. Both bindings pass `typedBlockViewOptions` to the renderer, like `collection`:

```ts
compositeNodes({
  typedBlock: VueNodeViewRenderer(CompositeView, typedBlockViewOptions),
  dataView: VueNodeViewRenderer(DataView, typedBlockViewOptions),
  reference: VueNodeViewRenderer(ReferenceView),
})
```

## The query builder

In an editable editor the layout bar of a data view has an "Abfrage bearbeiten" button that opens the **query builder** (`QueryBuilder.vue` / `QueryBuilder.tsx`, both over `createQueryBuilderModel` in `views/query-builder-model.ts`; `createEditorQueryBuilder(editor, node)` wires it to a view). The form has:

- the **Block type** the query reads (a stored query that names several types keeps them until another type is chosen);
- a flat **AND list of conditions**, each `field`, `operator`, `value`. The fields are the properties of the type plus the pseudo-fields `$title`, `$tags`, `$type`, `$id`, `$doc` and `$basket` (the baskets are its choices). The operators are the `ops` of the field's kind in `PROPERTY_KINDS`, so a date offers `before`/`after`/`on`, a number `lt`/`gte`, a `status` or `select` its options as choices, a `checkbox` yes/no. Changing the field resets the operator to the first one of its kind;
- one **sort** field with a direction and one **group** field (with "hide the group without a value").

Every change builds the query again and runs `validateQuery`. Only a complete, valid query is written to the block's `meta.query` (through `setDataViewQuery`); a row without a value yet, or a query the validator refuses, writes nothing and the form says how many parts are open. The view then asks again at once because its node changed. What the form has no control for stays as stored: `search`, `aggregates`, `select`, `page`, the `scope`, sorts after the first, and a filter with `or` or nesting (the form shows it as kept and offers to replace it). A change of the stored query from outside (another user, an undo) is followed by the open form.

```ts
import { createEditorQueryBuilder } from '@skaile/forge-editor/composites'

const builder = createEditorQueryBuilder(editor, node)   // null without a block ID or a translator
builder?.setBlockType('task')
const id = builder!.addCondition()
builder!.setConditionField(id, 'status')
builder!.setConditionValue(id, 'done')                   // writes `status eq done`
builder!.setGroup('$basket')
```

## `/ansicht`

The slash command `/ansicht` inserts a data view after the caret's block over the Block type that has the most Typed blocks in the open Document (the first one met wins a tie; a data view never counts). A Document without another Typed block gets a view over the default Block type of the settings. `insertDataView(editor, fallbackType)` does the same from code, and `insertComposite(kind, meta)` takes the `meta` of the block it creates.

The Block type's own slash entry (`/dataView`) is not offered: an empty view says nothing, `/ansicht` always starts with a query.

## The Typed blocks table is the `table` layout

`CompositeTable` (Vue and React) lists the Typed blocks of the open Document. It does not have a table of its own: its columns are those of `VIEW_LAYOUTS.table` (`tableColumnsOf`), its filters are one `DataQuery` that `evaluateQuery` answers over `rowsAsBlockRows(rows)`, and cell text and sort keys come from the kind registry. `tableViewOf` adds what only the panel has (tags, summary, search box, the sort the header sets). A column or kind added to the layout shows in the panel and in every data view.


## Sitemap

See the full [sitemap](https://skaile-ai.github.io/forge-editor/sitemap.md) for all pages.
