This sample shows how to create own controls in a package project and how to use them in an HMI project: the recommended way with ES modules, the classic way with the global TcHmi namespace, the different kinds of module imports and unit tests for a control.
- Open ExampleControls.sln in Visual Studio or the TwinCAT XAE Shell with TwinCAT HMI Engineering installed.
- Restore the NuGet packages, build the solution and start the Live-View of the
HMIproject. - Switch Value at the bottom and watch the color of the controls on the pages
ColorChangeandClassic. Open the pageModule importsto see the imported values.
The solution contains three projects:
| Project | Content |
|---|---|
| ExampleControls | Package project with the four example controls. |
| HMI | HMI project which uses the controls. |
| UnitTests | Unit tests for the control ColorChange. |
The package contains these controls:
| Control | Shows |
|---|---|
| ColorChange | The recommended way: an ES module with an attribute, which imports the framework API. |
| ColorChangeClassic | The same control the classic way: a TypeScript namespace with the global TcHmi API. |
| ModuleImports | The different kinds of module imports in TypeScript. |
| ModuleImportsJs | The same imports in plain JavaScript, without TypeScript. |
Desktop.view uses the layout of the TwinCAT HMI BaseTemplate from the Beckhoff.TwinCAT.HMI.BaseTemplate package: a header at the top, a navigation on the right, a region for the pages in the center and a container at the bottom. The pages ColorChange, Classic and Module imports explain the controls with code excerpts. The switch at the bottom changes the internal symbol ColorChangeValue, which is bound to the attribute Value of ColorChange and ColorChangeClassic.
ColorChange shows a circle whose color depends on its boolean attribute Value:
- ColorChange.ts is an ES module. It imports the framework API (
TcHmiControl,Controls,ValueConverter,EventProvider) fromBeckhoff.TwinCAT.HMI.Framework/index.esm.jsand registers the control withControls.registerEx. Description.json listsColorChange.jswith the typeEsModule. Description.jsondefines the attributedata-tchmi-valuein the propertyattributes, with its type, its default value and the names of its setter and getter.- The setter
setValueconverts the new value withValueConverter.toBooleanand returns if it did not change. Otherwise it stores the value, raisesonPropertyChanged, so bindings which read the attribute are updated, and calls__processValue. The gettergetValuereturns the stored value. __processValuetoggles the HTML classesglow-greenandglow-redon the circle element.- The CSS files of the themes Base and Base-Dark define the colors of these classes. The theme independent Style.css defines the shape and fades between the colors.
Before ES modules, controls were written as TypeScript namespaces which use the global TcHmi object. Such controls keep working, but new controls should be ES modules. ColorChangeClassic is ColorChange written this way:
| Topic | ColorChange | ColorChangeClassic |
|---|---|---|
| Code | ES module, export class ColorChange |
namespace TcHmi.Controls.ExampleControls { export class ... } |
| Framework API | import { ValueConverter, ... } from 'Beckhoff.TwinCAT.HMI.Framework/index.esm.js' |
Global TcHmi.ValueConverter, TcHmi.EventProvider, ... |
| Base class | TcHmiControl.Control |
TcHmi.Controls.System.TcHmiControl |
| Registration | Controls.registerEx(..., ColorChange) |
TcHmi.Controls.registerEx(..., TcHmi.Controls.ExampleControls.ColorChangeClassic) |
| Compilation | tsconfig.json of the package, "module": "es2022" |
Own tsconfig.json in the control folder, "module": "none" |
| Loading | Description.json: type EsModule |
Description.json: type JavaScript |
A project can contain several tsconfig.json files: TwinCAT HMI Engineering adds each TypeScript file to the nearest tsconfig.tpl.json and tsconfig.json in its folder or above. The page Classic shows ColorChangeClassic and compares its code with ColorChange.
ModuleImports and ModuleImportsJs are the same control, written in TypeScript and in JavaScript. Both show the values they import. The comments in ModuleImports.ts and ModuleImportsJs.js explain each import in detail.
- Relative path:
import { randomNumber, badNamedNumber as betterName } from "./someNumbers.js";imports named values, renamed or not, andimport defaultNumber from "./someNumbers.js";the default export. The path ends with.js, because the browser loads the compiled JavaScript file. - Bare module name:
import { TcHmiShortName } from "textmodule";uses a name instead of a path. The propertyesmoduleImportsin Manifest.json maps the name to a file for the browser (an import map), per control withmoduleScopeforModuleImportsJs. For TypeScript,pathsintsconfig.jsonandtsconfig.tpl.jsonmap the same name. - Framework API:
import { TcHmiControl, Controls } from "Beckhoff.TwinCAT.HMI.Framework/index.esm.js";imports the API of the framework inModuleImports.ts. The names of all packages are added to the import map automatically.ModuleImportsJs.jsuses the globalTcHmiobject instead, for exampleextends TcHmi.Controls.System.TcHmiControl: for plain JavaScript, Visual Studio reads notsconfig.jsonorjsconfig.json, so IntelliSense only knows the globalTcHmiobject fromTcHmi.d.tsand nothing about the imports.
Only the main file of a control is listed in the dependencyFiles of its Description.json with the type EsModule. The browser loads the imported files by itself.
The HMI project can import from the modules of a package as well: usesPackageImport.ts imports a number from ExampleControls/ModuleImports/someNumbers.js with the package name as path and writes it to the console of the browser. The path is mapped in tsconfig.json for TypeScript; the browser gets the package names in its import map automatically.
UnitTests tests ColorChange in the browser with Jasmine. Its README describes how to install and run the tests.
- Controls (documentation in Infosys)
