This concept page explains why dynamic expressions exist, how the two expression notations differ, and what mental model you need to reason about them effectively. For step-by-step configuration instructions, see the relevant How-To Guides.
Why Dynamic Expressions Matter
Consider a domain model where DesignRequirement entities link to SystemRequirement entities. Without dynamic expressions, a constraint that filters design requirements to a specific component would need to be hardcoded:
constraints:
load:
document:
component: "Braking"
This works for one document but breaks the moment you open a different component’s requirements. Dynamic expressions solve this by letting the configuration ask the runtime for the correct value:
constraints:
load:
document:
component: $context.source.document.component
Now the same configuration works across every component — Braking, Steering, Powertrain — because the filter value is resolved from the source entity’s actual document at the moment the sheet loads.
The same principle applies throughout sheet configuration: filtering queries by URL parameters, calculating column values from other properties, rendering custom HTML in cells, and conditionally styling rows based on data thresholds.
Two Notations, Two Worlds
Powersheet uses two distinct expression notations. Each belongs to a different configuration file type, and they are not interchangeable.
| Notation | Syntax | Configuration File | Evaluation Model |
|---|
| Context expression | $context.property.path | Domain model YAML (constraints) | Simple property path traversal |
| Dynamic value | () => expression | Sheet configuration YAML (where, formula, render, etc.) | JavaScript arrow function |
If you are editing a domain model YAML file (domainModelTypes, relationships), use $context. If you are editing a sheet configuration YAML file (sources, columns, formatters), use () =>.
Context Expressions: Property Path Traversal
Context expressions ($context.property.path) are declarative lookups. Think of them as a pointer into a data structure — no logic, no computation, just “go to this address and return what you find.”
Powersheet resolves a $context expression by walking the dot-separated segments at runtime. The expression $context.source.document.component means: start at the context object, navigate to source, then document, then read the component property.
This simplicity is intentional. Domain model constraints define structural rules about which entities can relate to each other. They should be predictable and side-effect-free. A constraint that runs arbitrary JavaScript could introduce subtle bugs across the entire traceability structure.
Available paths for $context:
| Path | Description | Example Value |
|---|
$context.source.type | Source entity’s work item type | "sys_req" |
$context.source.document.id | Source entity’s document ID | "Requirements/SRS" |
$context.source.document.moduleName | Source entity’s document module name | "UserNeedSpecification" |
$context.source.document.moduleFolder | Source entity’s document folder | "Requirements" |
$context.source.document.component | Source entity’s document component | "Braking" |
$context.source.document.type | Source entity’s document type | "systemRequirementsSpecification" |
$context.source.document.title | Source entity’s document title | "System Requirements" |
Dynamic constraints with $context are evaluated per-row. If your sheet displays system requirements from multiple components, each row resolves $context.source.document.component independently. Row A might resolve to “Braking” while Row B resolves to “Steering.”
Dynamic Values: JavaScript Arrow Functions
Dynamic values (() => expression) bring full JavaScript expressiveness to sheet configuration. They can access multiple context properties, perform calculations, construct strings, render HTML, and apply conditional logic.
The () => syntax is a JavaScript arrow function that receives a context object. At runtime, Powersheet evaluates the function and uses the returned value:
# A formula that multiplies two properties
formula: "() => context.item.quantity * context.item.unitPrice"
# A where clause that reads a URL parameter
where:
Client:
"==": "() => context.parameters.client"
Because these are real JavaScript functions, you have access to standard JavaScript capabilities: string interpolation, array methods, Date objects, arithmetic, and conditional (ternary) operators.
The Context Object
Both expression notations draw from a shared runtime context object. Understanding its structure is the key to writing correct expressions. The context is hierarchical — properties are progressively available depending on where the expression is evaluated.
context
|-- parameters URL and configuration parameters (key-value pairs)
| +-- {paramName} e.g. context.parameters.client
|-- user Current logged-in user
| +-- id, name
|-- sources All configured data source definitions
|-- document Current document information
| +-- title, type, id, moduleName, moduleFolder, component
|-- tool Current tool information
| +-- type
|-- item Current entity (per-cell contexts only)
| +-- {propertyName} e.g. context.item.StoryPoints
|-- source Parent/source entity (relationship contexts)
| +-- {propertyName} e.g. context.source.document.component
|-- entity Current entity as a plain object
+-- value Current cell's display value
Not all properties are available everywhere. The context is scoped based on where the expression runs:
| Usage Location | .user | .sources | .document | .item | .value | .source | .entity |
|---|
where | Yes | Yes | Yes | — | — | — | — |
entityFactory | Yes | Yes | — | — | — | — | — |
formula | Yes | Yes | — | Yes | Yes | Yes | Yes |
render / renderers | — | — | — | Yes | Yes | — | — |
formatter | — | — | Yes | Yes | Yes | — | — |
display | — | — | — | Yes | Yes | Yes | — |
Think of this as a funnel: at the query level (where), there is no “current item” yet — the query finds items. At the cell level (formula, render), each expression runs in the context of a specific item and cell value.
Referencing context.item inside a where clause will return undefined because the item has not been resolved yet at query time. Similarly, context.document is not available in render expressions. Always consult the availability table above when writing expressions.
Where Each Notation Is Used
Context Expressions in Domain Model Constraints
Context expressions appear exclusively in domain model constraint definitions — the rules that control which entities can be loaded or picked when navigating relationships.
relationships:
- from: DesignRequirement
to: SystemRequirement
back:
name: designRequirements
constraints:
load:
document:
component: $context.source.document.component
This constraint says: when expanding SystemRequirement to show linked DesignRequirement entities, only load those from documents matching the source’s component. The constraint is structural — it shapes what data appears in the sheet.
For more on how constraints work in the domain model, see Process Constraints and Entity Types and Relationships.
Dynamic Values in Sheet Configuration
Dynamic values appear across several sheet configuration properties, each serving a different purpose:
| Property | Purpose | Persists Data? |
|---|
sources.query.where | Filter which entities appear in the sheet | No |
sources.entityFactory | Set initial property values for newly created items | Yes |
columns.*.formula | Calculate a column value from other properties | Yes |
columns.*.render | Custom HTML rendering for cell display | No |
columns.*.display | Override the display value for navigation properties | No |
renderers.* | Named renderer definitions reusable across columns | No |
formatters.*.expression | Boolean condition for conditional styling | No |
formula and render look similar but behave very differently. A formula writes the calculated value back to the data source — it changes persisted data. A render only affects visual display. If you want to show a computed value without modifying underlying data, use render. If the computed value should be saved as part of the entity, use formula.
Formatter expressions use a simplified syntax that differs from both $context and () =>:
formatters:
criticalHighlight:
expression: "context.item.Probability <= 99"
style: warningStyle
Notice: no () => prefix, no $context prefix. The expression is evaluated directly as a boolean condition. This is a deliberate design choice — formatters only need to answer “does this condition match?” and the simplified syntax makes that intent clear.
Common Expression Patterns
Filtering by URL Parameters
URL parameters let external links or widgets pass filter values into the sheet:
sources:
- id: opportunities
query:
from: Opportunity
where:
Client:
"==": "() => context.parameters.client"
A URL like ?client=Acme would filter the sheet to show only Acme’s opportunities.
Calculating Derived Values
Formulas can reference any property on the current item:
columns:
totalPrice:
formula: "() => context.item.quantity * context.item.unitPrice"
Dynamic Initial Values
When users create new entities from within the sheet, entityFactory sets sensible defaults:
entityFactory:
Client: "() => context.parameters.client"
Custom Cell Rendering
Renderers produce HTML for rich cell content:
renderers:
linkedItems: "() => context.value.map((item) => `<b>${item.name}</b>`).join(', ')"
Date Comparisons
JavaScript Date objects work naturally in where clauses:
where:
DueDate:
">": "() => new Date().toISOString()"
Date values must be in the correct format for the target data type. For Polarion date fields, .toISOString() produces the expected format.
Mental Model: Static Structure vs Dynamic Behavior
The two expression types reflect a fundamental architectural division in Powersheet:
-
The domain model defines what exists — entity types, relationships, cardinality rules. It is the structural blueprint. Context expressions (
$context) fit here because they are declarative property lookups that shape data boundaries.
-
The sheet configuration defines how things look and behave — column layout, formatting, calculations, rendering. Dynamic values (
() =>) fit here because they need the full expressiveness of runtime computation.
This separation means you can change how data is displayed (sheet config) without affecting how data is structured (domain model), and vice versa. The domain model remains a stable foundation that multiple sheet configurations can build upon.
For deeper exploration of this separation, see Data Model vs Sheet Configuration and Model-Driven Design.
Quick Reference
| I want to… | Notation | Example |
|---|
| Filter relationships by source document | $context | component: $context.source.document.component |
| Filter query by URL parameter | () => | "==": "() => context.parameters.client" |
| Calculate column from other fields | () => | formula: "() => context.item.qty * context.item.price" |
| Render custom HTML in a cell | () => | render: "() => '<b>' + context.value + '</b>'" |
| Set default value for new items | () => | entityFactory: { Client: "() => context.parameters.client" } |
| Conditionally style a cell | expression | expression: "context.item.Risk > 50" |
Further Reading