> ## Documentation Index
> Fetch the complete documentation index at: https://docs.insight.nobly.dk/llms.txt
> Use this file to discover all available pages before exploring further.

# Templates and syntax

> Learn how Caseflow templates work — types, selection, syntax for variables, conditionals, loops, and the scope model.

## Template types

Caseflow templates come in four types:

| Type             | Purpose                                                                                                     |
| ---------------- | ----------------------------------------------------------------------------------------------------------- |
| **Create**       | Rendering for new objects                                                                                   |
| **Update**       | Rendering for existing objects                                                                              |
| **Preview Pane** | Compact, read-oriented view used in the search preview panel                                                |
| **Portal**       | Application-level pages with no backing primary object — see [Portal templates](/caseflow/portal-templates) |

## Template selection

When no explicit `templateName` is supplied, selection uses class and template type with access-aware resolution:

1. **Group-based match first** — user group assignment is evaluated, and the highest-precedence matching template is selected
2. **Default fallback** — if no group match exists, the default template is used

If an explicit template name is supplied, that template is fetched directly.

See [Administration and best practices](/caseflow/administration) for template governance details.

## Template syntax

### Variable resolution

Use `{"{{...}}"}` for render-time template variables:

```html theme={null}
{{AttributeName}}
{{stock.objectid}}
{{user.username}}
{{alias.AttributeName}}
```

The engine supports nested paths using dot notation.

### Conditionals

```html theme={null}
{{#if condition}}...{{/if}}
{{#if condition}}...{{else}}...{{/if}}
{{#if condition}}...{{else if condition}}...{{else}}...{{/if}}
```

Condition expressions support:

* Comparisons: `==`, `!=`, `>`, `<`, `>=`, `<=`
* Logic: `&&`, `||`, `!`
* Grouping with parentheses

### Loops

```html theme={null}
{{#each items as item}}...{{/each}}
{{#each items as item, index}}...{{/each}}
```

Loop sources can be arrays, comma-separated strings, or objects (iterated as key/value entries).

### Components and HTML

Templates can mix regular HTML tags with registered custom components (capitalized names). Unknown components render as explicit error placeholders.

### Property parsing and type coercion

Component props are parsed from strings and coerced where possible:

* JSON objects and arrays are auto-parsed when valid
* `"true"` / `"false"` are converted to booleans
* Numeric literals are converted to numbers

This is why template props can be authored as JSON strings in many component attributes.

## Template directives

Templates can include HTML comment markers that influence the rendering host's behavior. These are parsed and stripped before rendering — they do not appear in the rendered output.

| Directive                             | Effect                                                                                                                                                                       |
| ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `<!-- workview:hideDefaultSubmit -->` | Suppresses the default Create/Save submit button on the host modal so the template can supply its own submit control (typically a `SaveButton` configured for the workflow). |

<Note>
  Directive identifiers are technical literals parsed by the template runtime. They retain the `workview:` prefix for backwards compatibility.
</Note>

### Example

```html theme={null}
<!-- workview:hideDefaultSubmit -->
<div class="space-y-4">
  <EditableField attributeName="Name" required="true" />
  <EditableField attributeName="Description" type="textarea" />

  <div class="flex justify-end gap-2">
    <SaveButton scope="primary" label="Submit application" />
  </div>
</div>
```

## Template shell and styling

Every Caseflow template renders inside a wrapper element with the `data-workview-template-shell` attribute. Inside the shell, the platform applies a small CSS reset so that templates can use standard utility classes like Tailwind's `.container` without being constrained by the responsive max-width rules used elsewhere in the application:

* `.container` inside a template shell expands to 100% width
* Default container margins are removed inside the shell

This means template authors can use Tailwind layout primitives directly without having to wrap content in a `ResponsiveContainer` component. The selector name is a technical literal retained for backwards compatibility.

## Scope model

Template scope contains two namespaces:

| Namespace | Keyed by                          |
| --------- | --------------------------------- |
| `ids`     | Attribute IDs                     |
| `names`   | Attribute names and known aliases |

Common values added to scope:

* Regular object attributes
* Stock attributes (`stock.*`)
* Object metadata (`ObjectId`, `ClassName`)
* User context (`user.id`, `user.username`, etc.)
* Alias objects registered via `RelatedObject`

## Render-time vs runtime variables

This is an important distinction:

| Form          | Resolution moment       | Typical sources                                              |
| ------------- | ----------------------- | ------------------------------------------------------------ |
| `{"{{...}}"}` | Template rendering      | Object attributes, stock attributes, aliases, user info      |
| `${"${...}"}` | Action/script execution | Change context, selected objects, row context, script result |

**Rule of thumb:**

* Use `{"{{...}}"}` for static template composition and default values
* Use `${"${...}"}` for event-driven logic and action conditions

### Runtime variable families

| Context                            | Variables                                                   |
| ---------------------------------- | ----------------------------------------------------------- |
| `EditableField` `onChanged`        | `${newValue}`, `${previousValue}`                           |
| `RelationPicker` `onChanged`       | `${newValue}`, `${previousValue}`, `${selectedObject...}`   |
| `ObjectSearch` row document config | `${row.stock.objectid}`, `${row.AttributeName}`             |
| Save hooks                         | `${changedFields}`, `${oldValues.X}`, `${newValues.X}`      |
| Script actions                     | `${scriptResult}`, `${scriptResult.path}`, `${scriptError}` |
| Date/time                          | `${today}`, `${now}`                                        |
| Create actions                     | `${createdObjectId}`, `${objectKey}`                        |

### Mixed render-time and runtime example

```html theme={null}
<EditableField
  attributeName="Status"
  onChanged='createObject:{"className":"AuditLog","attributes":{"ObjectId":"{{stock.objectid}}","OldValue":"${previousValue}","NewValue":"${newValue}"}}'
/>
```

Here `{"{{stock.objectid}}"}` is resolved when the template renders, while `${previousValue}` and `${newValue}` are resolved when the action executes.

## Conditional expressions

Actions can be guarded with a `when` clause:

```text theme={null}
actionExpression when conditionExpression
```

### Comparison operators

| Operator | Description           |
| -------- | --------------------- |
| `==`     | Equal                 |
| `!=`     | Not equal             |
| `gt`     | Greater than          |
| `lt`     | Less than             |
| `gte`    | Greater than or equal |
| `lte`    | Less than or equal    |

### String operators

| Operator     | Description        |
| ------------ | ------------------ |
| `contains`   | String contains    |
| `startsWith` | String starts with |
| `endsWith`   | String ends with   |

### Unary operators

| Operator     | Description        |
| ------------ | ------------------ |
| `isEmpty`    | Value is empty     |
| `isNotEmpty` | Value is not empty |
| `isTrue`     | Value is truthy    |
| `isFalse`    | Value is falsy     |

### Logical operators

Combine conditions with `and`, `or`, `not`, or `!`.

### Examples

Workflow transition rule:

```html theme={null}
<EditableField
  attributeName="Status"
  onChanged='setValue:{"attributeName":"ClosedDate","value":"${today}"} when ${newValue} == "Closed" and ${previousValue} != "Closed"'
/>
```

Escalation rule:

```html theme={null}
<EditableField
  attributeName="Priority"
  onChanged='createObject:{"className":"EscalationLog","attributes":{"ObjectId":"{{stock.objectid}}","Priority":"${newValue}"}} when ${newValue} contains "Critical"'
/>
```

## Nullish coalescing

Use `??` in runtime expressions to guard optional relation data:

```html theme={null}
<RelationPicker
  relationAttrName="TypeRelation"
  displayAttributeName="Name"
  onChanged='setValue:{"attributeName":"Status","value":"${selectedObject.DefaultStatus ?? \"New\"}"}'
/>
```

## Action design guidance

**Recommended:**

* Prefer pipe-delimited action chains for readability: `action1 | action2 | action3`
* Keep each action idempotent where possible
* Use explicit `queryKey` values in all search components that need external refresh
* Use `throwError` in `onBeforeSave` for business-rule enforcement

**Avoid:**

* Long opaque action chains with no conditional guards
* Writing non-persisted temporary values to normal attributes
* Mixing row-level `${row.*}` expressions outside row-driven contexts
