Code Canvas

Prev Next

Code Canvas

Code Canvas creates a custom widget from an HTML template, CSS, and data. It uses Handlebars expressions to insert values into the HTML. See Handlebars documentation for more details: https://handlebarsjs.com/guide/

You can also add JavaScript helpers to format values or control the template output. These helpers do not run JavaScript in the Portal page.

Code Canvas is currently an Alpha widget for dashboards and reports.


Configuration overview

The current editor uses configuration sections.

  • Title: The widget title and its visibility.

  • Data Sources: The data sources and items that the template can read.

  • HTML Template: The HTML, Handlebars expressions, and CSS that define the widget content.

  • Custom Helpers: Optional JavaScript functions for the template.

  • Documentation: Optional help text for people who view the widget.


Title and frame

The Title section contains these fields:

  • Title: The name of the widget.

  • Show title: Controls whether the widget shows its title.

The Title section appears during widget creation. For an existing widget, the dialog header shows the current title.

To change an existing title, select Edit title from the menu next to the dialog title.

The dialog also provides a Show frame control where the layout supports frames. Code Canvas has no frame by default.


Data Sources

The Data Sources section defines which data items the template can read.

The browser can show points, attributes, calculations, statistics, or metrics, according to the source content.

  • Use the search field to find a source or an item.

  • If the source is not available, use the action menu to link a data source to the app.

  • Select an item from the source browser.

  • Repeat the selection for each item that the template needs.

The table under the browser shows the items:

  • Data name: The selected item name or alias.

  • Type: The request type that the widget stores for the item.

  • Interval: The interval associated with the selection, if one exists.

  • Remove data source: Removes that row from this widget. It does not remove the source from the app.

A template that contains only static HTML does not require a data source.


HTML Template

The Template section contains the required HTML template field.

The template combines:

  • HTML: Defines the content and structure.

  • Handlebars: Inserts values, repeats content, and selects content through conditions.

  • CSS: Defines the appearance through style attributes or a style element.

If the template is empty, the editor offers Load example. The example creates a table for each selected data source.

The example uses the available row fields. It does not require a specific point name or data-source key.

The widget waits until data loading finishes before it creates new template output.


Template data

The template receives four root values:

  • data: The data-source responses, grouped by data-source key.

  • timestamp: An ISO timestamp generated for the template context. This is not a measurement timestamp.

  • title: The fixed text CodeCanvas Widget. This is not the title from the Title section.

  • isLoading: The data-loading flag. The widget does not create new output while this flag is true.

The template does not receive separate root values for the selected time range, selected assets, or widget dimensions.

The data-source responses contain the returned rows. Each row can contain a timestamp, an object ID, and data columns.

The exact columns depend on the data source and the selected items.


Read data-source rows

The data object uses data-source keys, not source display names. The dataSourceEach helper reads the results from a source response.

For a specific source, use this expression:

{{#dataSourceEach (lookup data "YOUR-DATA-SOURCE-KEY")}}

Replace YOUR-DATA-SOURCE-KEY with the actual key of the selected source.

Inside this block, the current context is one result row. A column name refers to the value in that row.

This example shows an ActivePower column with one decimal place:

{{#dataSourceEach (lookup data "YOUR-DATA-SOURCE-KEY")}}
  <p>
    {{formatDateTime @timestamp "en-US"}}:
    {{formatNumber ActivePower 1}} kW
  </p>
{{else}}
  <p>No data is available.</p>
{{/dataSourceEach}}

Replace ActivePower with the actual column name. Use the exact spelling and capitalization of the returned column.

The dataSourceEach block provides these variables:

  • @index: The row position, starting at zero.

  • @key: The same value as @index.

  • @first: True for the first row.

  • @last: True for the last row.

  • @timestamp: The parsed timestamp from the row, or null if the row has no timestamp.

  • @objectId: The object ID from the row.

If the source has no results, the helper shows the content after {{else}}.

For a column name that contains spaces or punctuation, use lookup:

{{lookup this "Active Power"}}

To read every selected source without a specific source key, use an outer each block:

{{#each data}}
  {{#dataSourceEach this}}
    <p>Object: {{@objectId}}</p>
  {{else}}
    <p>No data is available for this source.</p>
  {{/dataSourceEach}}
{{/each}}

If no sources exist, the outer each block produces no content.


Built-in helpers

Built-in helpers are available without entries in the Helpers section.

  • Template structure: each, if, unless, with, and lookup.

  • Data-source rows: dataSourceEach.

  • Number format: formatNumber, formatPercent, and formatCurrency.

  • Arithmetic: add, subtract, multiply, divide, modulo, round, ceil, floor, abs, min, and max.

  • Dates and times: formatDate, formatTime, formatDateTime, timeAgo, now, and year.

  • Comparisons: eq, ne, lt, lte, gt, and gte.

  • Logic: and, or, and not.

  • Conditional blocks: ifCond, switch, case, and default.

  • Text: uppercase, lowercase, capitalize, trim, truncate, and replace.

  • Arrays: length, first, last, join, sum, and average.

  • Repeated content: times and range.

  • HTML attributes and CSS: classNames, attr, dataAttr, style, and styles.

  • Other functions: defaultValue, json, log, and debug.

These examples show common helper calls:

  • {{formatNumber ActivePower 2}} shows a number with two decimal places.

  • {{formatPercent Availability 1}} multiplies a fraction by 100. A value of 0.95 becomes 95.0%.

  • {{formatCurrency Revenue "USD"}} formats a currency value.

  • {{formatDateTime @timestamp "en-US"}} formats a date and time with the specified locale.

  • {{defaultValue Status "Unknown"}} replaces null, undefined, or an empty string. It preserves zero and false.

  • {{json data true}} shows the response structure as formatted JSON.

The sum and average helpers expect arrays of numbers. They do not extract a column from an array of result rows.

A comparison can control a template block:

{{#if (gt ActivePower 1000)}}
  <p>Power exceeds 1000 kW.</p>
{{else}}
  <p>Power does not exceed 1000 kW.</p>
{{/if}}

Helpers

The Helpers section adds custom Handlebars helpers. These helpers are JavaScript functions that the template calls by name.

To add a helper:

  • Select Advanced mode.

  • Open Helpers.

  • Select Add helper.

  • Select Simple or Block.

  • Enter a unique Name.

  • Enter an optional Description.

  • Enter the JavaScript function in Code.

  • Select Add.

The Usage column shows a syntax hint. The function parameters determine the arguments that the template must supply.


Simple helpers

A Simple helper accepts arguments and returns a value.

This helper formats a value with one decimal place:

function(value) {
  return Number(value).toFixed(1);
}

For a helper named toFixed1, the template call is:

{{toFixed1 ActivePower}}

The Simple examples are uppercase, percentage, and statusColor.

Use a distinct helper name to avoid replacing a built-in helper.


Block helpers

A Block helper controls the content between an opening expression and a closing expression.

The final function argument is the Handlebars options object:

  • options.fn(context): Creates the main block content with the supplied context.

  • options.inverse(context): Creates the content after {{else}}.

  • options.hash: Contains named arguments, such as limit=100.

  • options.data: Contains private template variables.

This helper selects content through a threshold comparison:

function(value, threshold, options) {
  return value > threshold
    ? options.fn(this)
    : options.inverse(this);
}

For a helper named ifGreater, the template call is:

{{#ifGreater ActivePower 1000}}
  <p>Power exceeds the threshold.</p>
{{else}}
  <p>Power does not exceed the threshold.</p>
{{/ifGreater}}

The Block examples are bold, ifGreater, list, and withIndex.


Helper validation and limits

The editor requires a valid name and function before it permits Add or Save.

  • A helper name has a maximum of 50 characters.

  • The first character must be a letter, an underscore, or a dollar sign.

  • Later characters can also include numbers.

  • A name must be unique within the custom helper list.

  • Reserved names, such as if, each, and lookup, are not permitted.

  • The function code has a maximum of 10,000 characters.

  • The code must be a valid JavaScript function expression. Standard functions and arrow functions are supported.

Helpers run in a separate worker thread. They have no access to the page DOM.

Validation rejects prohibited code patterns for network requests, browser storage, timers, module imports, and global object access.

Validation also rejects eval, new Function, and obvious infinite loops. A helper is not a place for page scripts or external library imports.


CSS and widget size

CSS can define spacing, colors, borders, and layout. The template supports inline styles and style elements.

Use a distinctive class name for the template container. Prefix CSS selectors with that class to avoid changes to other page content.

This example uses a Portal theme variable for its border:

<div class="my-code-canvas">
  <p>Custom widget content</p>
</div>

<style>
  .my-code-canvas {
    padding: 1rem;
    border: 1px solid var(--p-content-border-color);
  }
</style>

Theme variables help the widget match light and dark themes.

The current Code Canvas editor has no Height field. The widget container has a minimum height of 400 pixels.

CSS can control the size of the template content. It does not remove the minimum height of the widget container.


HTML restrictions

The widget sanitizes the HTML output before it shows the content.

Supported content includes headings, paragraphs, lists, tables, links, images, style elements, and a limited set of SVG elements.

The sanitizer removes script elements, event-handler attributes, and other unsupported HTML.

Custom JavaScript cannot attach page events, draw on a canvas, or start a chart library through the template.

Custom helpers prepare template output. They do not create an interactive JavaScript application inside the widget.


Documentation

The Documentation section adds help text for the widget. This text is separate from the HTML template and does not change its output.

  • Select Advanced mode.

  • Open Documentation.

  • Enable Enable Documentation Tooltip.

  • Enter the help text in Description.

The tooltip appears through an information icon next to the widget title.


Apply the configuration

For a new widget, Next and Back move between the configuration steps. Add appears at the final step.

For an existing widget, Apply accepts the changes. Cancel closes the dialog without applying the current edits.

The Add or Save button inside a helper dialog accepts that helper draft only. The main widget dialog still requires Add or Apply.


Common problems

  • Helpers or Documentation is missing: Select Advanced mode.

  • The title fields are missing: Select Edit title from the menu next to the dialog title.

  • The widget has no content: Verify that HTML template contains content.

  • The template shows no rows: Verify that the widget contains selected data items.

  • A specific source shows no rows: Verify the data-source key in lookup.

  • A value is blank: Verify the column name and capitalization against the returned data.

  • The interval is read-only: New single-aggregated selections do not permit interval edits in the selected-item table.

  • A helper cannot be saved: Correct the name or code errors shown in the helper dialog.

  • The widget shows Render error: Correct the template or helper identified by the error message.

  • The widget shows Request timeout: Reduce the amount of data or simplify the helper code.

  • A script or event handler does not work: The widget removes unsupported page scripts and event-handler attributes.