Skip to main content

Template types

Caseflow templates come in four types:
TypePurpose
CreateRendering for new objects
UpdateRendering for existing objects
Preview PaneCompact, read-oriented view used in the search preview panel
PortalApplication-level pages with no backing primary object — see 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 for template governance details.

Template syntax

Variable resolution

Use {"{{...}}"} for render-time template variables:
{{AttributeName}}
{{stock.objectid}}
{{user.username}}
{{alias.AttributeName}}
The engine supports nested paths using dot notation.

Conditionals

{{#if condition}}...{{/if}}
{{#if condition}}...{{else}}...{{/if}}
{{#if condition}}...{{else if condition}}...{{else}}...{{/if}}
Condition expressions support:
  • Comparisons: ==, !=, >, <, >=, <=
  • Logic: &&, ||, !
  • Grouping with parentheses

Loops

{{#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.
DirectiveEffect
<!-- 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).
Directive identifiers are technical literals parsed by the template runtime. They retain the workview: prefix for backwards compatibility.

Example

<!-- 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:
NamespaceKeyed by
idsAttribute IDs
namesAttribute 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:
FormResolution momentTypical sources
{"{{...}}"}Template renderingObject attributes, stock attributes, aliases, user info
${"${...}"}Action/script executionChange 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

ContextVariables
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

<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:
actionExpression when conditionExpression

Comparison operators

OperatorDescription
==Equal
!=Not equal
gtGreater than
ltLess than
gteGreater than or equal
lteLess than or equal

String operators

OperatorDescription
containsString contains
startsWithString starts with
endsWithString ends with

Unary operators

OperatorDescription
isEmptyValue is empty
isNotEmptyValue is not empty
isTrueValue is truthy
isFalseValue is falsy

Logical operators

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

Examples

Workflow transition rule:
<EditableField
  attributeName="Status"
  onChanged='setValue:{"attributeName":"ClosedDate","value":"${today}"} when ${newValue} == "Closed" and ${previousValue} != "Closed"'
/>
Escalation rule:
<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:
<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