mySCADA
myDESIGNER › myDESIGNER › Scripts & Programming

JavaScript in Custom Components

On this page

Every custom component in myDESIGNER can contain a JavaScript script that controls its behaviour at runtime (in HMI / mySCADAweb). The script runs inside the SVG engine and has full access to the mySCADA runtime API.


The Three Required Functions

The component script is an object with three lifecycle functions. myDESIGNER generates this template automatically:

this.init = function (svg, component, properties, values) { }   // called once, after the view loads
this.datarefresh = function (data) { }                          // called on every data change
this.remove = function () { }                                   // called when the view is closed

Additional custom functions can be added in the same way:

this.myHelper = function (param) { }
// call it as:  this.myHelper(someValue);

Lifecycle

StepWhat happens
1The SVG view is rendered.
2After the first render (requestAnimationFrame), the runtime sets this.myscada on each component and calls init(...).
3On every data update (tag change, zoom, view variable change), datarefresh(data) is called — only if the component has tags configured (see section 4).
4When the view is closed, remove() is called; the runtime then deletes myscada, init, and all other properties from memory.

Important: all three calls are wrapped in try { } catch (e) { } by the runtime. A script error is silently swallowed — the component simply stops working. During development, use console.log and add your own try/catch with console.error(e).


init(svg, component, properties, values)

Called once after the view loads. All initialisation belongs here: reading properties, creating DOM elements, starting timers and event listeners.

ParameterWhat it is
svgThe root <svg> element of the view. Use svg.getElementById(id) to find elements.
componentThe <g> SVG element that is the component container — draw into this.
propertiesComponent variables set in myDESIGNER: an object { list: [ { variableName, type, value, translations }, … ] }. Read values with this.myscada.getCompVariable(properties, "name").
valuesValues for object-type properties (item lists, etc.) set in myDESIGNER. Log it with console.log(values) before using it.

Reading the component’s size

The most reliable way — works even when the component is hidden:

var bbox = this.myscada.getBoundingBox(component);   // returns {x, y, width, height}

getBoundingBox tries getBBox() first; if it returns zeros (hidden element), it falls back to the inkscape:componentsize attribute and then getBoundingClientRect(). Note: values from componentsize are strings — use parseFloat() before doing arithmetic.

Element ID prefix

All element IDs inside a component are prefixed so multiple instances of the same component on one view don’t collide. Always use the prefix when accessing elements:

this.myscada.setText("prefix_id_text0001", "value");

Any elements you create dynamically in init should also get unique IDs (e.g. "myRect_" + id).


this.myscada — the Runtime API

this.myscada is an instance of the mySCADA runtime library (SVGJavascript.js). Every function is also available with the myscada prefix (myscada.setText = myscada.myscadaSetText). Async variants exist for async operations (e.g. writeTagsAsync).

Component properties

FunctionDescription
getCompVariable(properties, "name")Value of a component variable by its variableName. For type 7 (selection), returns the text before the first ;. Returns "" if the variable doesn’t exist.
getCompVariableTranslation(properties, "name")Same as above but returns the translation for the current language.

Values returned by getCompVariable are always strings ("1", "0", "12"). For booleans compare with == "1"; for numbers use Number() / parseInt() / parseFloat().

var fontSize = this.myscada.getCompVariable(properties, "fontsize");
var bold     = this.myscada.getCompVariable(properties, "bold") == "1";
var color    = this.myscada.getCompVariable(properties, "color");

DOM and visual manipulation

FunctionDescription
getElementById(id)Returns a DOM element by ID.
getObject(id)Returns the component object (.objekt) by ID — e.g. a combo or input; call its methods.
getSVG()Returns the root <svg> element.
getText(id) / setText(id, value)Read/write text of an SVG text element. id may also be an array of IDs.
setTooltip(id, text)Sets a hover tooltip.
replaceValueForHash(id, value)Fills a formatted value in place of ##.# inside a text element.
setFillColor(id, color) / setStrokeColor(id, color)Fill / stroke color.
setOpacity(id, 0..1)Opacity.
setWidth(id, w) / setHeight(id, h)Dimensions.
setRotationAngle(id, angle, x, y)Rotation around a point.
setScale(id, type, sx, sy)Scale.
setAbsoluteMoveXY(id, x, y) / setRelativeMove(id, pathId, value)Move an element.
circleSector(id, percent, angleFrom, angleTo)Draw a circle sector.
showElements([id…]) / hideElements([id…]) / setVisibleOn(id) / setVisibleOff(id)Visibility.
showLayer(id) / hideLayer(id) / getLayer(name)Layers.
setErrorFrameOn(id) / setErrorFrameOff(id)Red error frame around an element.
setUserData(id, data) / getUserData(id)Arbitrary user data on an element.
doClick(id)Triggers a click on an element.
getBoundingBox(el)Element bounding box (see above).
createElement(type, {attributes})Creates an SVG element in the root SVG.
removeElement(id)Removes an element.
repaint()Forces a repaint.

Reading and writing PLC tags

FunctionDescription
getTagValue("tag@script")The last loaded value of a tag (null if not yet loaded). Returns an array when there are multiple values. Also works for expressions ("=..."). The tag must be read in the current view.
getTagStatus("tag@script")Read status (1 = OK, < 0 = error).
getTagInfo("tag@script")Full tag object (val, status, errcnt, …).
writeTags({name, values}, callback)Write to a tag table defined in server-side scripts; callback(err, data).
setTag(tag, plc, value) / toggleTag / incTag / decTagWrite/modify a tag via the model view.
writeTagsForBatchID(id, [values], callback)Write using a Write ID (WRID) from a component animation — see section 5 below.

View script variables

FunctionDescription
getVariable("name")Value of a view script variable (0.0 if it doesn’t exist).
setVariable("name", value)Sets a variable and triggers an animation refresh.
setVariables({a: 1, b: 2})Same for multiple variables at once.
viewScriptVariable("a.b", [value])Read/write nested properties (a.b.c).

Built-in system variables (read via getVariable): runCount, loggedUser, loggedUserLevel, loggedUserGroups, activeAlarmsCount, nonAckAlarmsCount, viewname, browserIsMobile, dateFrom, dateTo, dateRange, mouseX, mouseY.

Other useful function groups

GroupFunctions
LanguagegetLanguage(), setLanguage(lang), getString(key)
Auth & rightsloginAs, logout, loginLogout, changePassword
NavigationopenView, openParametricView, openViewIntoActiveArea, openTrend, openDocument, openPreviousView, …
Dialogsalert, listDialog, closeDialog, alarmConfirm
Inter-view messagingsendObject, sendObjectToView, sendObjectToActiveArea
Server communicationsendDataToServerSideScript, sendDataToServerSideScriptAuth, getFileFromUrl, sendDataToUrl
AlarmsreadAlarmsOnline, readAlarmsHistory
Data logsreadDatalogData, insertRowIntoDatalog
Charts & tablescreateChart, createTable, createPDF, createTimeline
Zoom & scrollzoomToArea, getZoom, setScrollEnabled
FilesgetUserFilesListing, getFileContentFromUserFiles, getFileContentFromUserData
StoragestoreObject(key, obj), retrieveObject(key)
UtilitiesgetTime, getFormattedDate, wait(ms) (async), sum, mean, quantile, compareArrays, getMD5

datarefresh(data)

Called on every data update. The frequency is throttled by the runtime (roughly 20 ms – 1 s depending on rendering load), so this function must be fast — no heavy operations, no creating many DOM elements on every call.

When it is called: only for components that have a tag list configured in myDESIGNER (animation type chart). A component with no tags never receives datarefresh — use your own timer instead (see example 3 below).

What data is: an array with one entry per tag in the component’s tag list, in the same order:

// component tags: [ "temperature@plc1", "pressure@plc1", "=a+b" ]
this.datarefresh = function (data) {
    var temperature = data[0][0];   // data[i] is an ARRAY of values; first value is [0]
    var pressure    = data[1][0];
    var expression  = data[2][0];   // expressions (starting with '=') are wrapped in a single-element array
};
  • For tags that return multiple values (e.g. arrays), all values are in data[i].
  • If a tag is not found or not loaded, the runtime logs an error to Messages and data may be incomplete — always guard against undefined.

Recommended skeleton with guard against early calls and unnecessary redraws:

this.datarefresh = function (data) {
    if (!this.ready) return;                            // datarefresh may arrive before init finishes
    var v = data[0] && data[0][0];
    if (v === undefined || v === this.lastV) return;    // nothing new → do nothing
    this.lastV = v;
    this.myscada.setText("prefix_id_text0001", v);
};

Code Examples

Example 1 — Display a tag value in a text element

this.init = function (svg, component, properties, values) {
    this.ready = true;
};

this.datarefresh = function (data) {
    if (!this.ready || !data[0]) return;
    this.myscada.setText("prefix_id_text0001", data[0][0] + " °C");
};

this.remove = function () { };

Example 2 — Change colour based on a threshold (reading a component property + tag)

this.init = function (svg, component, properties, values) {
    this.limit = Number(this.myscada.getCompVariable(properties, "limit"));
    this.ready = true;
};

this.datarefresh = function (data) {
    if (!this.ready) return;
    var v = data[0][0];
    this.myscada.setFillColor("prefix_id_rect0001", v > this.limit ? "#ff0000" : "#00aa00");
};

this.remove = function () { };

Example 3 — Custom timer and cleanup (component without tags)

A component with no tag list never receives datarefresh. Use setInterval to poll a tag that is already being read in the view:

this.init = function (svg, component, properties, values) {
    this.timer = setInterval(function () {
        var v = this.myscada.getTagValue("temperature@plc1");   // tag must be read somewhere in the view
        if (v !== null && v !== undefined) {
            this.myscada.setText("prefix_id_text0001", v);
        }
    }.bind(this), 1000);
};

this.datarefresh = function (data) { };

this.remove = function () {
    clearInterval(this.timer);   // REQUIRED — otherwise the timer keeps running after the view closes
};

Example 4 — Embedding an HTML element inside SVG (foreignObject)

this.init = function (svg, component, properties, values) {
    var bbox = this.myscada.getBoundingBox(component);

    // remove the placeholder rect/text AFTER reading bbox
    while (component.hasChildNodes()) component.removeChild(component.firstChild);

    var fo = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject");
    fo.setAttribute("x", bbox.x);
    fo.setAttribute("y", bbox.y);
    fo.setAttribute("width",  Math.round(bbox.width));
    fo.setAttribute("height", Math.round(bbox.height));

    this.input = document.createElement("input");
    this.input.type = "text";
    this.input.style.width    = "100%";
    this.input.style.fontSize = this.myscada.getCompVariable(properties, "fontsize") + "pt";
    this.input.style.color    = this.myscada.getCompVariable(properties, "textcolor");

    this.onChange = function () {
        this.myscada.setVariable("myVariable", this.input.value);
    }.bind(this);
    this.input.addEventListener("change", this.onChange);

    fo.appendChild(this.input);
    component.appendChild(fo);
    this.ready = true;
};

this.datarefresh = function (data) { };

this.remove = function () {
    if (this.input) this.input.removeEventListener("change", this.onChange);
    this.input = null;
};

Example 5 — Writing a value to a PLC tag

To write a value to a PLC tag in response to a user action, use myscada.setTag(...) or myscada.writeTags(...) (for a tag table defined in server-side scripts):

this.init = function (svg, component, properties, values) {
    this.tag = this.myscada.getCompVariable(properties, "tag");
    this.plc = this.myscada.getCompVariable(properties, "plc");

    var btn = this.myscada.getElementById("prefix_id_btn");
    this.onClick = function () {
        this.myscada.setTag(this.tag, this.plc, 1);
    }.bind(this);
    btn.addEventListener("click", this.onClick);
    this.btn = btn;
};

this.datarefresh = function (data) { };

this.remove = function () {
    if (this.btn) this.btn.removeEventListener("click", this.onClick);
    this.btn = null;
};

remove() — Cleanup

Remove everything you started in init:

  • clearInterval / clearTimeout for every timer
  • removeEventListener for listeners attached outside the component (document, svg, window)
  • WebSockets or other connections (ws.close())
  • References to DOM and to this.myscada (this.myscada = null, this.input = null)

After remove(), the runtime deletes myscada and init. Any callback that fires later (e.g. a timer you forgot to clear) will find this.myscada gone and silently fail.


Common Pitfalls

  1. Errors are invisible. init, datarefresh, and remove are all wrapped in try/catch. Debug with console.log and your own try/catch with console.error(e).
  2. Property values are strings. "0" is truthy in JavaScript. Compare booleans with == "1" and convert numbers with Number() / parseFloat().
  3. Always use .bind(this) on callbacks inside setInterval, addEventListener, or promise chains — otherwise this is not the component.
  4. Avoid global variables. All state belongs on this.
  5. Use unique element IDs (with the component’s prefix) — the same component can appear multiple times in one view.
  6. Keep datarefresh fast. Do not create DOM elements or overwrite innerHTML on every call. Compare the new value against the last before touching the DOM.
  7. getTagValue returns null until the tag has been loaded (or if the view does not read it at all). Always guard against null.
  8. getBBox() may return zero on a hidden element. Use myscada.getBoundingBox() instead.
  9. Read inkscape:label and bounding boxes before removing child nodes — removeChild deletes them.
  10. Components inside a nested <svg> are not initialised by the runtime (init is only called for components in the main view SVG).

Checklist Before Publishing a Component

  • What does the component display, and which component variables (properties) does it need?
  • Does it need tags (→ will receive datarefresh), or does it read data itself (→ needs its own timer)?
  • In init: read properties, bounding box, and inkscape:label before creating or removing child elements.
  • All dynamically created elements have unique IDs with a prefix.
  • datarefresh is fast, guards against undefined, and only touches the DOM when the value changes.
  • remove cancels all timers, listeners, and connections.
  • Tested with console.log open in the browser DevTools, because script errors are otherwise silent.