Skip to main content
Ctrl+K
Syside - Home Syside - Home
  • About
  • Editor
  • Modeler
  • Automator
  • API
    • Troubleshooting
    • Licensing
    • Offline license
    • Forum
    • Changelog
latest0.11.00.110.11.00.100.10.30.90.9.10.80.8.80.70.7.20.60.6.4
  • About
  • Editor
  • Modeler
  • Automator
  • API
  • Troubleshooting
  • Licensing
  • Offline license
  • Forum
  • Changelog

Section Navigation

Get started

  • Install Modeler
  • Write your model
  • Visualize your model

Views

  • Diagram views Labs
    • Open a diagram
    • Use diagram panels
    • Select diagram elements
    • Configure diagram views
  • Grid views: tables and matrices Labs
    • Use grid views
    • Foundations
    • Table views
      • Get started with table views
      • Table view structure
      • Navigation chains
      • Navigation step reference
      • Representations
      • Common patterns
      • Complete examples
      • Migrate pre-0.11 grid views
    • Matrix views
    • Troubleshoot grid views

Command line

  • Modeler CLI
    • Install Modeler CLI
    • Quickstart
    • Diagram generation Labs
    • CLI commands
    • Configure Modeler CLI

Configuration

  • Configure Modeler
  • Settings in syside.toml
  • Settings in the editor
  • Modeler
  • Grid views: tables and matrices
  • Table views
  • Navigation chains

Navigation chains

A cell often needs to show something that lives near the row element rather than on it: the subject of a requirement, the value of a named attribute, the definition typing a usage. A navigation chain, written in a column’s columnFeature, is how the column walks from the row element to that related element. This page explains how chains are built. The exact steps and their parameters are listed in Navigation step reference.

A chain starts from baseViewElement, the row’s own element, and wraps it in navigation steps. Each step takes an element and navigates to elements related to it, such as Owner(…) for the owning element or NamedUsage(…, "mass", ...) for a feature by name. The first argument of a step is what it navigates from, and the remaining arguments are parameters:

// the "torque" attribute of each row element, looked up by name
:>> columnFeature = NamedUsage(baseViewElement, "torque", NameType::name);

Steps nest, and a chain reads from the inside out. This chain hops from each part usage to the definition typing it, then on to that definition’s torque:

// the "torque" attribute of the definition typing each row element
:>> columnFeature = NamedUsage(
    DefinitionsFirst(baseViewElement),
    "torque",
    NameType::name
);

Show one element, list several, or split the row

Some navigations can only ever find one element: an element’s owner, a requirement’s subject, a feature looked up by name. Those come as a single step, such as Owner or NamedUsage, and the result can always be navigated further.

Other navigations may find several elements: the definitions typing a usage, the supertypes of a definition, the ports of a part. Each of those comes as a family of three steps, and the suffix says what happens to the targets it finds:

Suffix

What it does with the targets it finds

First

Expects exactly one target and lets further steps chain onto it. If the navigation finds more than one, the cell shows the first with a warning instead of quietly picking one (the truncated outcome).

List

Renders every target together in one read-only cell. A list cannot be navigated onwards, so a List step is always the last in its chain.

Fan

Turns the row into one sub-row per target. Because this changes the shape of the whole table, a Fan step must be named at view level. See Split a row into sub-rows.

HeritageList, HeritageFan, and HeritageFirst navigate identically and take the same parameters. Only the handling of their targets differs.

Share one chain between columns

A chain does not have to be written out inside the column that uses it. Declaring a named navigation at view level gives a chain a name that any column can start from:

view enginesTable : TVD::TableView {
    expose DroneProject::Fleet::*;

    engine = NamedUsage(
        baseViewElement,
        "engine",
        NameType::name,
        UsageKind::partUsage
    );

    view 'Engine Type' :> columnViews {
        :>> columnFeature = DefinitionsFirst(engine);
        attribute :>> featureRepresentation :> ColRep::declaredName;
    }
    view 'Engine Power' :> columnViews {
        :>> columnFeature = NamedUsage(
            engine,
            "power",
            NameType::name,
            UsageKind::attributeUsage
        );
        attribute :>> featureRepresentation :> ColRep::featureValue;
    }
    view 'Engine Documentation' :> columnViews {
        :>> columnFeature = engine;
        attribute :>> featureRepresentation :> ColRep::specificDocumentation;
    }
}

All three columns are about the engine, but the step that finds it is written once. Engine Type continues to the definition typing the engine, Engine Power to one of its attributes, and Engine Documentation uses the navigation directly.

Referencing a named navigation is exactly equivalent to writing its chain out in place, so it changes nothing about the rendered table. It is worth reaching for whenever several columns share a prefix: correcting the shared part later means editing one line rather than every column that repeated it.

A named navigation can hold any chain, with two limits. It has to navigate somewhere: alias = engine; or root = baseViewElement; only renames something that already has a name, and is rejected with a hint to reference the original directly. And named navigations may build on one another, as a fan-out that continues another does, but not in a cycle.

Split a row into sub-rows

Every step so far produces at most one cell per row. A Fan step is different: it turns one row into several, one per target it finds. A table whose rows are parts can show one row per port of each part, while still showing each part’s own values alongside.

Take a model with a pump whose two ports share a definition, where only inlet sets the maxPressure it declares, and a gauge with no ports at all:

port def FluidPort {
    attribute maxPressure;
}

part def Station {
    part pump {
        port inlet : FluidPort {
            attribute :>> maxPressure = 5.0;
        }
        port outlet : FluidPort;
    }
    part gauge;
}

The view fans each part’s row out over its ports:

ports = UsageFan(baseViewElement, UsageKind::portUsage);

view Part :> columnViews {
    attribute :>> featureRepresentation :> ColRep::declaredName;
}
view Port :> columnViews {
    :>> columnFeature = ports;
    attribute :>> featureRepresentation :> ColRep::declaredName;
}
view 'Max Pressure' :> columnViews {
    :>> columnFeature = NamedUsage(
        ports,
        "maxPressure",
        NameType::name,
        UsageKind::attributeUsage
    );
    attribute :>> featureRepresentation :> ColRep::featureValue;
    attribute :>> defaultValue = "(unset)";
}

Over that model, the view renders:

Part     Port      Max Pressure
─────────────────────────────────
pump     inlet     5.0
         outlet    (unset)
gauge    N/A       N/A

Three things in that picture are what fanning out means:

  • The fan-out has a name. Splitting rows affects the whole table rather than one column, so a Fan step cannot sit inline in a column. It is declared as a named navigation, here ports, and such a name is called an anchor. Port and Max Pressure both point at the anchor, which is what puts their cells on the same sub-rows. Part never references it, so it stays one cell per part, spanning the rows its ports produced.

  • outlet and gauge are empty for different reasons. outlet exists but sets no value, so its cell shows the column’s defaultValue. gauge has no ports at all, so there is no port for either fanned cell to be about, and those cells show notApplicableValue (see Cell outcomes).

  • gauge still has a row. A fan-out never removes rows. A parent whose fan-out found nothing keeps exactly one row, so nothing silently disappears from the table.

Anchors

An anchor is what makes several columns share the same sub-rows, so a few rules follow from that role.

The Fan step is always the outermost call of the anchor, never nested inside another step. Columns then reference the anchor and apply only non-fanning steps to it, as Max Pressure does with NamedUsage(ports, …). Columns that share an anchor are aligned by it: on any given row, their cells describe the same port. Two columns that fan over the same feature through separate anchors are not aligned, so reusing one anchor is the way to put several columns on one fanned target.

A table can fan out more than once by starting one anchor from another, for example one row per item of each port:

ports = UsageFan(baseViewElement, UsageKind::portUsage);
items = UsageFan(ports, UsageKind::itemUsage);

Anchors therefore form a chain, each starting from baseViewElement or from exactly one other anchor. What a table cannot do is fan out in two directions at once. Two anchors that both start from baseViewElement would make every combination of two unrelated sets, so the table reports an error instead of rendering (see The whole table is disabled).

Sub-rows appear in the order their targets are declared in the model, so row order is stable across reloads. In a hierarchical table a fanned row’s sub-rows are siblings at the same tree position, and the node’s children hang off the last of them. The tree column itself may never fan out.

Fanned tables on screen

That is everything needed to write a fan-out. What remains is how a fanned table behaves once it is on screen. Open whichever question comes up.

Merged cells

A cell covering several sub-rows renders once at the start of its run and spans them. How finely a column’s cells split depends only on how many fan-outs its chain contains, not on where the column sits: reordering columns, whether through columnOrder or by dragging, changes no row and no cell value. Merging is by identity, not by matching text: two ports that happen to share a name are never merged into one, and a repeated value on genuinely different targets stays repeated.

Editing a merged cell edits the one element behind it, so it is a single change rather than one per spanned row. Fanned cells write to their own target: an edit in Max Pressure lands on that row’s port.

Sorting, filtering, and export

Fanned rows change what these operations mean, in ways that keep the table’s structure intact:

  • Sorting by a fanned column reorders the sub-rows inside each parent’s run rather than across the whole table, so rows are never torn out from under the cell spanning them.

  • Filtering applies to the fanned rows. Sub-rows that do not match drop out, and a parent drops too once none of its sub-rows survive. This is distinct from a fan-out that found nothing, which keeps its row.

  • CSV export writes the flattened table: one line per row, with a spanning value repeated on every line it covered, the way a pivot-table export does.

For the columns most tables need, Common patterns has each chain ready to copy.

previous

Table view structure

next

Navigation step reference

On this page
  • Show one element, list several, or split the row
  • Share one chain between columns
  • Split a row into sub-rows
    • Anchors
    • Fanned tables on screen

Feedback

Report an issue

Legal

Third Party Licenses

Privacy Policy

Company

Visit Sensmetry

© 2025-2026 Sensmetry. All rights reserved.