Event Code
Event code is a short JavaScript code that runs when a widget is clicked or its value changes. The code runs not on the server but in the browser of the user who opened the page. It is used for tasks such as writing to more than one tag with a button, making decisions based on a value or applying a recipe. Event code is not needed to write a value to a single tag; the Process Value binding of the widget is enough.
Writing to the field
The write functions in event code send the value directly to the device in the field. If the code writes to the wrong tag or with the wrong value, the machine can run unexpectedly. Try the code in a safe environment before using it in the field. For writes that need user confirmation, call the setTag function with this and turn on the Needs Confirmation setting on the widget. Event code is not used for safety or interlock functions; these functions are provided in the PLC and the safety circuit.
Event code runs with the permissions of the user who opens the page
Event code runs with the session and all permissions of the user who opened the page; the permissions of the person using the page at that moment apply, not those of the person who wrote the code. When a user with administrator permissions opens the page, the code runs with these permissions too. Event code is saved with the page. From 2.11, only SystemAdmin accounts can add or change event code; users with the Editor role can see the code and delete it (see Who can add event code).
Widgets That Support Event Code
| Widget | Events |
|---|---|
| Button, Toggle button, Switch, Card, Box | onClick, onMouseDown, onMouseUp |
| Button group, Check box, Radio group | onClick, onMouseDown, onMouseUp, onChange |
| Drop-down | onChange |
| Event | When it runs |
|---|---|
onClick | When the widget is clicked. |
onMouseDown | When the mouse button is pressed. On the Button, Toggle button, Switch and Button group also when a finger lands on a touch screen (see Event code on touch). |
onMouseUp | When the mouse button is released. On the Button, Toggle button, Switch and Button group also when the finger lifts from a touch screen or the press is canceled. |
onChange | When the value of the widget changes. The new value is read with event.value. On Check box and Drop-down it can also run without a user action when the value of the bound tag changes; from 2.11, the write functions cannot be used in such a run (see Code that runs without a user action). |
For the list of widgets and their settings, see the Widget Reference page.
Mouse events run from 2.10.1
On the Check box, Switch, Radio group and Button group, onMouseDown and onMouseUp code runs from 2.10.1. onMouseDown and onMouseUp code written on these widgets in an earlier version starts running on every mouse press and release after the upgrade; if such code writes a tag, review it before the upgrade.
Event code runs only in run mode: in the viewer, and in the editor when design mode is turned off. In design mode, clicks do not run the code. On widgets that take input, event code does not run either while the Read Only setting is on.
Button
On a Button widget added from the palette, Read Only is on by default. Turn this setting off for the event code to run. If the Action Mode writes and the Process Value is bound to a tag, this value is written first on click, then the onClick code runs.
The composite component itself has no Events section. The event code of the widgets inside it can be edited separately in each copy if it is exposed in the component template as a parameter of type Code. See Component template editor.
Who Can Add Event Code
Event code runs in the browser of every user who opens the page, with that user's session and permissions. Therefore, from 2.11, only accounts in the SystemAdmin group can add or change event code. The rule is enforced on the server and applies wherever event code is saved:
- saving a page and creating a new page,
- importing a pages package with Import Pages (.zip),
- saving a page template or composite component template, Save Page As Template and importing a template package,
- restoring the Pages or Page Templates module from a configuration backup.
Parameters of type Code in composite components are event code as well: the Default Value in the template and the value entered in copies.
Users other than SystemAdmin (for example users with the Editor role):
- See the existing event code; the saved code keeps running.
- Can make and save every change that does not change code. Operations that change the whole page content can change code too: a Find and replace that changes a text inside event code (for example a tag name) counts as a code change (see Find and replace).
- Can delete event code, and move and copy a widget with code within the same page. The code of widgets pasted from another page or taken in with Import from JSON File counts as new code unless the same text is in the saved version of this page.
- Cannot add new code or change existing code. Creating a page from a template that contains event code, or adding such a template to a page, also needs SystemAdmin; linked composite component copies are outside this rule because their code comes from the saved template.
For these users, the Events section and the Event Code Edit window are shown read-only, with the note Only SystemAdmin can add or change event code. You can view and remove it. below them. The Code parameters of a composite component and the Default Value field of a Code parameter in the component template are read-only too, with the note Only SystemAdmin can change code parameters. below them.
If the saved content contains new or changed event code, nothing is saved and the message Adding or changing event code requires SystemAdmin rights. The change was not saved; you can still remove existing event code or move and copy it within the same page. is shown. The server compares the new content with the saved content: if the text of the code already exists in the saved page or in the template of a linked composite component on the page, it does not count as a change. A package import or a restore is rejected as a whole, even if only one page or template breaks this rule.
Page changes made with an AI assistant (MCP) follow the same rule. Before the assistant applies a change that adds, changes or deletes event code, it must show the full new code and ask for a separate confirmation; do not confirm without reading the code.
Event Code Editor
Event code is written in the Events section of the right panel while the widget is selected. There is a separate field for each event the widget supports. The header of the field shows the name of the event (for example, onClick), with these buttons on the right:
| Button | Function |
|---|---|
| Insert Tag (tag icon) | Opens the tag selector; the selected tag is inserted at the cursor position. |
| Snapshot name builder (clock icon) | Builds a consumption or history snapshot name and inserts it at the cursor position. |
| Open in a dialog (pencil icon) | Opens the code in the larger Event Code Edit window. |
| Delete | Deletes the code of this event. |
The Event Code Edit window also has the Insert Tag and snapshot buttons. Changes made in the window are transferred to the code with Ok and discarded with Cancel. Event code is saved with the page; after a change, save the page with Save.
Autocomplete
While you type, the editor suggests SCADA runtime functions:
- Typing
this.lists the available fields:srt(SCADA runtime),twCommon(the widget itself) andeditorOptions(editor state). - Typing
this.srt.lists the functions with their parameters. A short description and a usage example appear next to the selected suggestion. - Typing the beginning of a word (for example,
setT) suggests the full form of the function (this.srt.setTag(...)). The suggestion list can also be opened with Ctrl+Space. - When a function is inserted, its parameter fields are selected; move between the fields with Tab.
- Typing
this.srt.tag("…")., or a dot after a variable that holds the result ofthis.srt.tag(...), suggests the fields of the tag object (see Reading tags). - Typing
this.twCommon.properties.value.lists the properties of the edited widget. - No suggestions are shown inside strings (in quotes) and comments.
Tag Selector
The tag selector opens with the Insert Tag button or with Alt+T. When a function that takes a tag name (for example, tag, tagValue, setTag, addTag) is selected from the suggestion list, the tag selector opens automatically. The full name of the selected tag is inserted at the cursor position. If the cursor is inside quotes, the name is inserted as it is; otherwise it is inserted in quotes.
The system tags in the System tab of the tag selector are read-only. Therefore this tab is not shown for the tag field of the write functions.
Event Code Warnings
From 2.11, the editor lists risky calls in event code below the code under the heading Event code warnings. The warnings appear in the Events section and in the Event Code Edit window; each warning is shown with its Line number and a short suggestion. Warnings are given in these cases:
| Case | Suggestion |
|---|---|
A call that writes to the field without asking for confirmation and skips the widget settings, including setTag whose third parameter is not this. | Use this.srt.setTag(tag, value, this). |
In the onChange code of Check box and Drop-down, a function that cannot be used when the code runs without a user action (for example setTag, alarm and recipe apply functions). | On Check box, move write operations to onClick. A Drop-down has no onClick; check for a user selection before writing (see Writing from a Drop-down). |
Access to the whole page, the user's session data or browser storage, a network request outside this.srt, running code that is not visible in the editor. | Remove it if it is not needed; use the this.srt functions in event code. |
Warnings do not block saving and do not change the code; the code runs as written. If there are warnings when the page is saved, a short notification summarizes the number of warnings and the widgets concerned (with their names in the Elements tab). Names inside comments and texts (quotes) do not give a warning. No warning does not mean that the code is safe; the warnings only look for known names.
Writing Rules
Event code runs as the body of a function:
- Runtime functions are accessed with
this.srt, the widget itself withthis.twCommonand the editor state withthis.editorOptions. The event information is in theeventvariable; its content depends on the widget. awaitcan be used. The code can end early withreturn.- Before the code runs, line breaks are removed and the whole code becomes a single line. Therefore:
- end every statement with a semicolon (
;); - do not write line comments with
//; all the code after the comment is treated as a comment too. Use/* … */for comments.
- end every statement with a semicolon (
/* Correct: every statement ends with ; and the comment is a block comment */
const hiz = this.srt.tagValue("Hat1.Hiz");
if (hiz > 100) { this.srt.setTag("Hat1.Uyari", 1, this); }- If an error occurs, a short warning is shown to the user: An error occurred while running custom code and a short description of the error. The details of the error are written to the developer console of the browser (F12).
- The code is compiled on the first event of the widget. When the event code is changed in the editor, the new code is compiled on the next event; the page does not need to be reopened and the old code does not run.
SCADA Runtime Functions
The functions below are called with this.srt.. Functions not in the list are not supported for use in event code.
Reading Tags
| Function | Description |
|---|---|
tag(fullName) | Returns the tag object. If the tag does not exist, the result is undefined. |
tagValue(fullName) | Returns the current value of the tag. |
isNumberOrDefaultZero(value) | Converts the value to a number; returns 0 if it is not a number. |
Fields of the object returned by tag(): Value (value), PreviousValue (previous value), Quality (quality), ChangedTime (time of the last change), AlarmState (alarm state), Unit (unit), Description (description), Name (full name), Id (ID).
const t = this.srt.tag("Hat1.Hiz");
if (t?.Value > 100) { this.srt.setTag("Hat1.Uyari", 1, this); }A tag that is not bound returns empty on the first read
The browser receives values only for the tags the page reads. The values of tags used in widget bindings on the page arrive when the page opens. From 2.10.1, a tag that is read in event code with tag() or tagValue() and is not bound to any widget on the page, even when its name is built by concatenation, is added to the page's tag list on the first read: the first read returns empty, the value arrives shortly after, and later reads return the current value. If the code uses the value the first time it runs (for example code that runs once when the page opens), also bind the tag to a widget on the page (a hidden value label if needed). This needs both the server and the web interface at 2.10.1 or later; if either is older, a tag read in event code is not added to the list; its value arrives only if the tag is bound to a widget on the page. Snapshot names are an exception: a snapshot name written in full in quotes in event code (for example "Hat1.PLC.__hist.today.avg.Hiz") is found in the code when the page opens and is read without a binding; snapshot names built by concatenation are not found. System tags produced by the browser itself (for example, __activeAlarmCount, __currentUserRoles) can always be read.
Writing Tags
| Function | Description |
|---|---|
setTag(fullName, value, this) | Writes a value to the tag. If this is given as the third parameter, the settings of the widget are applied (table below). This is the recommended way to write. |
addTag({ Name: fullName, Value: value }) | Writes the value only to the tag list in this browser; it is not sent to the field. The page prefix is not applied. The value is kept when the live data connection drops and is restored; it is removed when the user logs out or the browser page is reloaded (including the automatic reload of a kiosk screen). Do not use the name of a tag that is defined on the server. |
When this is given to the setTag function, these checks are made:
| Widget setting | Effect |
|---|---|
| Read Only on | Not written. |
| Editor in design mode | Not written. |
| Needs Confirmation on | Before writing, the question Are you sure you want to proceed? is asked. |
If the tag name is empty, setTag does not write and shows the message TAG not found!.
These conditions apply to all write functions:
- There must be a server connection.
- The user must have the Operate or Editor role.
- The tag must not be read-only.
- For tags with the Pulse or Rising edge command behavior, the user must see the tag within their process group scope (see Tag Write Scope).
Writes made by users are recorded in Operation History together with the user who wrote them.
/* Start with confirmation: Needs Confirmation must be on for the widget */
this.srt.setTag("Hat1.Motor1.Start", 1, this);Write Result
The write result is reported to the user as a warning. This behavior is the same for writes from widgets and for the write functions in event code:
| Situation | Warning |
|---|---|
| No server connection | Write request not sent: no SCADA connection. |
| The user has no write role | Write request not sent: you are not allowed to write tags. |
| The server rejected the write | Write request rejected: … and the reason: no permission (role or process group scope), the tag is read-only, the tag was not found in the runtime, the tag was not loaded into the runtime (license or driver limit), the tag or value is missing, or the server could not pass the request to the driver (for example, a BACnet write rejected by the device) |
| The write was sent, but the device or tag quality is bad | Write request sent, but the device or tag quality is bad; the value may not reach the device. |
No message is shown for a successful write. "Sent" means that the request was passed to the driver; it does not show that the PLC or device applied the value. For critical commands, verify the result from the next value of the written tag (or of the PLC's feedback tag).
Alarm
The alarm functions require the user to have alarm permission.
| Function | Description |
|---|---|
ackAlarm(alarmId) | Acknowledges the alarm. If 0 is given, all alarms the user can see are acknowledged. |
ackSingleAlarm(alarmId) | Acknowledges only the given alarm; it never acknowledges all alarms. The alarm must be in the user's alarm list and wait for acknowledgement; with an empty, 0 or unknown id, or an alarm already acknowledged, no acknowledgement is sent. It uses the same permission check as the acknowledge button of the alarm table. Returns true when the acknowledgement was sent. |
muteAlarm(mute) | Turns the alarm sound off (true) or on (false) in this browser. |
lockAlarm(alarm) | Sends the lock state of the alarm row to the server. The Ignored field of the alarm object gives the desired state. |
this.srt.ackAlarm(0);For an id that comes from a tag, use ackSingleAlarm; for example a button that acknowledges the newest alarm (see Newest alarm):
this.srt.ackSingleAlarm(this.srt.tagValue("@NOP::__latestAlarmId"));Device and Connection
| Function / field | Description |
|---|---|
reInitDevice(deviceId) | Restarts the device. Requires the Operate or Editor role, and the device must be in the user's process group. |
reInitDataGroups() | Reloads the process group definitions. Requires the Editor or UserAdministrator role. |
triggerDatabaseScan(deviceId) | Starts a read scan immediately on a device of the Database protocol. Used with await. Requires the Editor role. |
connected | The state of the server connection. The value is read with .value. |
deviceStates | Device states. The value is read with .value. |
if (!this.srt.connected.value) { return; }
await this.srt.triggerDatabaseScan(3);Recipe
Recipe functions are used with await. To apply a recipe, the user must have recipe apply permission and the license must include the recipe feature.
| Function | Description |
|---|---|
applyRecipe({ RecipeId, ContinueOnError }) | Applies the recipe. If ContinueOnError: true, it continues without stopping at a failed item. FailedItemCount in the result is the number of items that could not be written. |
getRecipes(recipeGroupId, includeItems) | Lists recipes. The parameters are optional: the group ID and whether the items are also returned (default false). |
getRecipeGroups(includeTags) | Lists recipe groups. includeTags is true by default. |
const sonuc = await this.srt.applyRecipe({ RecipeId: 7, ContinueOnError: true });
if (sonuc?.FailedItemCount > 0) { console.warn(sonuc); }Interface
| Function | Description |
|---|---|
setLoadingPanel({ visible }) | Shows (true) or hides (false) the loading panel over the page. |
visualElements(widgetId, path) | Reads a property of another widget by widget ID and dotted property path. Returns 0 if the widget or property is not found. |
this.srt.setLoadingPanel({ visible: true });
try { await this.srt.applyRecipe({ RecipeId: 7 }); } finally { this.srt.setLoadingPanel({ visible: false }); }Widget and Editor Fields
| Field | Description |
|---|---|
this.twCommon.properties.value | The current properties of the widget, with bindings applied. |
this.editorOptions.editMode | true if the page is in design mode in the editor. |
Not Recommended
Other items not described on this page may also appear in autocomplete. They are not supported for use in event code. For page navigation, use the Navigation (To) settings of the widget instead of event code.
Code That Runs Without a User Action
The onChange code of Check box and Drop-down also runs without anyone clicking when the value of the bound tag changes (from the PLC or from another screen), and it is repeated on every screen that has the page open. From 2.11, this.srt is restricted in these runs: it contains only read functions and the local values of this browser, and it cannot write to the field or the server.
| Widget | onChange runs restricted | onChange runs with all functions |
|---|---|---|
| Check box | Always: the check state comes from the bound value, so also after a click | Never; put the work that depends on a click into onClick |
| Drop-down | When the bound value changes | When the user makes a selection in the list |
| Button group | It does not run; onChange runs only on a user selection | When the user clicks or taps a button or selects it with the Enter or Space key |
| Radio group | Does not run; onChange runs only on a user selection | When the user makes a selection |
The onClick, onMouseDown and onMouseUp code always runs with all functions.
Restricted this.srt:
| Functions | |
|---|---|
| Available | tag, tagValue, isNumberOrDefaultZero, connected, deviceStates, visualElements, addTag, recipe read functions (getRecipes, getRecipeGroups) |
| Not available | setTag, alarm functions (ackAlarm, ackSingleAlarm, muteAlarm, lockAlarm), applyRecipe, reInitDevice, reInitDataGroups, triggerDatabaseScan, setLoadingPanel and the other functions not described on this page |
If a function that is not available is called, the code stops at that line; the lines before it have already run. The user sees the warning An error occurred while running custom code with a description such as "srt.setTag is not available in event code without a user action (e.g. onChange after a bound value change). Move this call to onClick." The warning is shown once for the same widget and function until the browser page is reloaded; after a reload (including the automatic reload of a kiosk screen) it can be shown again. It is written to the browser console on every run. The editor shows these calls as a warning before saving.
addTag changes only the tag value in this browser; it does not write to the device or the server.
Behavior change on upgrade
Saved code that passes on the change of a tag by writing to another tag in the onChange code of these widgets stops writing in 2.11. To write on an operator action, move the code to onClick on Check box. A Drop-down has no onClick; a write in its onChange code keeps working when the user makes a selection in the list (see Writing from a Drop-down). From 2.10.1, the onChange code of the Button group runs only on a user selection and with all functions; it is not affected by this change. Pass values between tags in the PLC or on the server side; because the same code runs separately on every open screen, a transfer done in the browser repeats the write as many times as there are screens.
This restriction prevents accidental writes; it is not a security boundary. The code still runs with the session of the user who opened the page (see Who can add event code).
Writing from a Drop-down
The only event of a Drop-down is onChange; it has no onClick event. When the user makes a selection in the list, the onChange code runs with all functions and can write with setTag(..., this). When the bound value changes on its own, the same code runs restricted and stops at the write call. To avoid this stop and the warning, check before writing whether the code runs on a user selection. On a Drop-down, event.event is set only on a user selection:
/* The bound value changed and the user did not select: do not write */
if (!event.event) return;
this.srt.setTag("Hat1.Recete.Secim", event.value, this);On a Drop-down, the warning for a function that is not available and the editor warning suggest this check instead of onClick: add if (!event.event) return; before the call. A Button group does not need this check: its onChange code runs only on a user selection and with all functions. The onChange code of a Check box always runs restricted; on a Check box, write in onClick.
Tag Prefixes and @NOP::
If a Tag Prefix is defined for the page, the tag names written in event code are resolved relative to this prefix. This rule applies to tag, tagValue and setTag. addTag does not apply the prefix. On pages opened in a modal window, the prefix of the modal page is used. From 2.10.1, tag("…") and tagValue("…") in expressions in the Tag Name field of bindings also resolve relative names with the prefix of the page or modal that contains the expression; the Tag Prefix value of a composite component is applied only to names written directly or in single quotes (see Shared equipment window). For the definition of the prefix, see the Tag name and page prefix section.
| Written in code | Tag Prefix | Tag used |
|---|---|---|
"Hiz" | Hat1. | Hat1.Hiz |
".Hiz" | Hat1 | Hat1.Hiz (the leading dot becomes the separator) |
"Hat2.Hiz" | Hat1. | Hat1.Hat2.Hiz |
"@NOP::Hat2.Hiz" | Any | Hat2.Hiz |
"Hat2.Hiz" | None | Hat2.Hiz |
In event code, the prefix is also added in front of full names with dots. On a page with a prefix, to access a tag independent of the prefix, write @NOP:: in front of the name. The @NOP:: text is always removed, so it can be used safely on pages without a prefix too.
const sicaklik = this.srt.tagValue("@NOP::Genel.DisSicaklik");
const alarmSayisi = this.srt.tagValue("@NOP::__activeAlarmCount");The Tag Prefix value of a composite component is applied only to the tag bindings of the component; it is not applied to event code. In event code inside the component, write the tag name relative to the page prefix.
For snapshot names (__cons.…, __hist.…), the prefix rules are the same as on the Consumption Snapshots page.