UNPKG

node-red-contrib-uibuilder

Version:

Easily create data-driven web UI's for Node-RED. Single- & Multi-page. Multiple UI's. Work with existing web development workflows or mix and match with no-code/low-code features.

819 lines (505 loc) 69.1 kB
--- title: Functions available in the modern client description: | Details about the functions/methods used in the UIBUILDER front-end client library. Some functions are available to your own custom code and some are hidden inside the `uibuilder` client object. created: 2023-01-28 15:56:57 updated: 2025-03-29 13:31:33 --- Functions accessible in client-side user code. - [Receiving Messages from Node-RED](#receiving-messages-from-node-red) - [Sending Messages to Node-RED](#sending-messages-to-node-red) - [Variable Handling](#variable-handling) - [UI Handling](#ui-handling) - [HTML/DOM Cacheing](#htmldom-cacheing) - [Event Handling](#event-handling) - [Utility](#utility) - [Startup](#startup) - [Alphabetical function list](#alphabetical-function-list) > [!NOTE] > Where functions are marked as being accessible as Node-RED command messages, details can be found in [Controling From Node-RED](client-docs/control-from-node-red). ## Receiving Messages from Node-RED UIBUILDER automatically creates a live websocket channel from the Node-RED server to your browser when you open a UIBUILDER page. By sending a msg to a UIBUILDER node, it will be transferred to ALL connected clients unless you specify otherwise. To process a message in your browser, you can use one of the event handling UIBUILDER library functions such as [`onChange`](#event-handling) or [`onTopic`](#event-handling). Alternatively, you can use the standard `document.addEventListener` function to listen for one of the UIBUILDER library's custom events. You can also do `uibuilder.set('msg', {/*your object details*/})` in your front-end code which instructs the client to treat the object as though it had come from Node-RED. You can use one or more of the `msg._uib.pageName`, `msg._uib.clientId`, or `msg._uib.tabId` properties to control whether a specific page, client or browser tab will process an inbound message. Use this where you have multiple pages or clients and need to target a message to a specific one. UIBUILDER also allows you to issue control commands from Node-RED to your front-end app by sending messages to the `uibuilder` node containing `msg._uib.command` which must be set to [one of the recognised commands](/client-docs/control-from-node-red). > [!TIP] > As of v7, clients automatically _filter_ incoming messages based on `pageName`, `clientId`, and `tabId` properties either in `msg._ui` or `msg._uib`. This means that you can send messages to specific clients or pages without needing to filter them in your flows. This is particularly useful when you have multiple clients connected to the same Node-RED instance. ### Rooms Rooms are a Socket.IO server concept. For UIBUILDER, they are simulated on the client by using standard message titles. Their use allows flexible messaging from any uibuilder connected client or a custom uibuilder node to any other uibuilder connected client or custom uibuilder node. UIBUILDER clients are automatically subscribed to a number of rooms based on the client socket ID, page name and client ID. They are also subscribed to a "global" channel. See also [sendRoom](#sendRoom) for sending messages to an arbitrary room. #### `joinRoom(room)` - Join an arbitrary socket.io room to receive messages from it :id=joinRoom This simulates joining a specified room on the client side, uibuilder will start forwarding messages sent to the room to this client. > [!WARNING] > As yet, the client has no way of handling messages from the server in these rooms. #### `leaveRoom(room)` - Leave an arbitrary socket.io room to stop receiving messages from it :id=leaveRoom This simulates leaving a specified room on the client side, uibuilder will stop forwarding messages sent to the room to this client. ## Sending Messages to Node-RED ### `eventSend(domevent, originator = '')` - Send a standard message back to Node-RED in response to a DOM event :id=eventSend This is a convenience function that is useful to attach as an event handler on an HTML DOM event (e.g. the click event of a button). Since it only requires the DOM event object that the DOM provides automatically. In HTML code, use as `<button onclick="uibuilder.eventSend(event)">`. `onchange` and any other user input event types will work as well. If you prefer to work with JavaScript, you can use `$('#mybutton').addEventListener('click', uibuilder.eventSend)`. Note that event listeners are not prefixed with `on` and the event name is passed as a string. The output to Node-RED includes some additional useful event data such as what modifier keys were active (e.g. shift, ctrl, etc) when the event happened. If you want to add custom data to the response, you can add `data-xxxx` attributes to the originating HTML tag. These appear as named `msg.payload` properties. If the source event is generated by a tag inside an HTML `<form>`, the names and values for all input elements in the form at the time of sending will be attached as `msg.payload` properties. Extended data for all input elements inside the form are included in `msg._ui.form`. You would normally use a button for this such as `<button type="button" onclick="uibuilder.eventSend(event)">Submit</button>`. Noting that the button type must be `button` and not `submit` otherwise the form will be submitted over HTTP instead of using uibuilder's websockets. If the source event type is `change` (e.g. a user changed an input field and then moved to a new field), the value of the input is put into both `msg.payload.value` and `msg._ui.newValue`. If you want to report the old value as well, you need to add something like `onfocus="this.setAttribute('data-oldvalue', this.value)"` to the element's attributes. This would be included both in `msg.payload.oldvalue` and in `msg._ui.attribs.data-oldvalue`. Alternatively, `onfocus="this.uib_oldvalue = this.value"` would cause the previous value to be included as `msg._ui.props.uib_oldvalue`. > [!NOTE] > When using `eventSend` on a button within a form, the message will be sent even if the form is invalid. `msg._ui.form.valid` will be true or false so that you can check for validity within Node-RED. Additional validity data will be available within each returned `msg._ui.form` object of which there is one entry for each input type (including buttons). > > See [Zero-code element types > Forms](elements/forms) for more detail. > > The input element validity property can contain any of the following: badInput, customError, patternMismatch, rangeOverflow, rangeUnderflow, stepMismatch, tooLong, tooShort, typeMismatch, valueMissing. See [ValidityState](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState) for details of the meanings. > [!HINT] > This function is used automatically by uibuilder's form element type from the `uib-element` node. ### `beaconLog(txtToSend, logLevel)` - Send a short log message to Node-RED :id=beaconLog This has the advantage of working even if Socket.IO is not connected. It uses a logging API provided by UIBUILDER. However, only text strings can be sent and messages need to be kept short. It only works with modern browsers that support the web beacon API. The `logLevel` matches both Node-RED and UIBUILDER defined log levels (e.g. error, warn, info, debug, trace ). ### `getPageMeta()` - Requests the current page's static file metadata from the server :id=getpagemeta This is handled as a uibuilder control message. The return data is also a control message handled by the internal function `uibuilder._ctrlMsgFromServer()`. The returned data is set into the uibuilder [managed variable `pageMeta`](/client-docs/variables#read-only). ### `htmlSend()` - Sends the whole DOM/HTML back to Node-RED :id=htmlSend Results in a standard message sent to Node-RED where the `msg.payload` contains the whole current HTML page as a string. Use with the `uib-save` node to fix the latest page complete with any dynamic updates as the html page to load for new client connections for example. The returned message includes `msg.length` to allow checking that the returned html payload wasn't truncated by a Socket.IO message length restriction. This can also be triggered from Node-RED. Send a message to the `uibuilder` node containing `msg._uib.command` set to "htmlSend". [Reference](client-docs/control-from-node-red?id=get-complete-copy-of-the-current-web-page) ### ~~logToServer()~~ - Not yet reliable. Will cause the input to appear in Node-RED logs :id=logToServer ### `send(msg, originator = '')` - Send a custom message back to Node-RED :id=send The `msg` format is the same as used in Node-RED. The `originator` is optional and if used, should match the id from a `uib-sender` node. That allows you to specifically return a message into a flow that uses one of those nodes. However, ensure that the `uib-sender` node has turned on the flag to allow returned messages. ### `sendCtrl(msg)` - Send a custom control message back to Node-RED :id=sendCtrl The message will be assessed by UIBUILDER and passed to its #2 (bottom) output port if considered acceptible. This lets you create your own control custom messages should you wish to. Use with caution. ### `sendCustom(channel, msg)` - Send a message to a custom socket.io channel :id=sendcustom This allows for front-end browser code to send a message back to Node-RED on any arbitrary Socket.IO channel. This is only really useful for custom extension nodes for UIBUILDER. ### `sendRoom(room, msg)` - Send a message to an arbitrary socket.io room :id=sendRoom Socket.IO rooms are server-side concepts for partitioning messages. This simulates the feature on the client side. See also [joinRoom](#joinRoom) and [leaveRoom](#leaveRoom) for controlling receipt of room messages. ### `setOriginator(originator = '')` - Set/clear the default originator :id=setOriginator Will automatically be used by `send` and `eventSend`. Set to an empty string to remove. ### `setPing(ms)` - Set a repeating ping/keep-alive HTTP call to Node-RED :id=setPing This uses an HTTP API call to a custom UIBUILDER API endpoint in Node-RED. So it works even if the Socket.IO connection is not working. It is used to check that the Node-RED server and the UIBUILDER instance are both still working. #### Example ```javascript uibuilder.setPing(2000) // repeat every 2 sec. Re-issue with ping(0) to turn off repeat. // Optionally monitor responses uibuilder.onChange('ping', function(data) { console.log('pinger', data) }) ``` ### `uploadFile(file, meta)` - Upload a file to Node-RED over Socket.IO :id=uploadFile `file` must be a reference to a FileHandler API file object. These can be generated by `<input type="file">` or file drag/drop change events. `meta` is an optional object that can contain any additional metadata you want to send with the file. This is useful for sending information about the file such as its name, type, size, etc. The `eventSend` function now uses this to add useful information to the message sent to Node-RED. Converts the reference to a Buffer and attempts to send back to Node-RED as a standard message. > [!WARNING] > Socket.IO has a default maximum message size of 1MB. Any file close to this size may fail. The uibuilder client library will issue an error message if the message content is equal to or larger than the current `maxHttpBufferSize` setting. > > You can change this by setting an override in Node-RED's `settings.js` file and restarting Node-RED. See the [settings documentation](/uib-configuration#settingsjs) `socketOptions` for details. The client variable `uibuilder.maxHttpBufferSize` contains the current size setting. This is also shown in Node-RED in the initial "client connect" control message. ## Variable Handling > [!NOTE] > See [client variables](client-docs/variables) for details of what uibuilder variables are available. ### `copyToClipboard(varToCopy)` - Copy the specified uibuilder variable to the browser clipboard :id=copyToClipboard Can only be used as an event handler because browsers do not allow unrestricted JavaScript access to the browser clipboard. `varToCopy` has to be a variable specified in UIBUILDER. One that could be retrieved by `uibuilder.get('varToCopy')`. ```html <button onclick="copyToClipboard('version')">Copy UIBUILDER client version string to clipboard</button> ``` ### `get(prop)` - Get a uibuilder property :id=get This is the preferred method to get an exposed UIBUILDER variable or property. Do not try to access variables and properties directly unless explicitly shared in this documentation. This function can also be called from Node-RED via `msg._uib.command` - `get` with `msg._uib.prop` set to the variable name to get. #### Example ```javascript console.log( uibuilder.get('version') ) ``` ### `getManagedVarList()` - Get a list of all uibuilder managed variables :id=getManagedVarList A UIBUILDER managed variable is one that has been created with `uibuilder.set()` (or changed from Node-RED with the equivalent command msg). As such, it can be watched for changes with `uibuilder.onChange()`. This function can also be called from Node-RED via `msg._uib.command` - `getManagedVarList`. The returned `msg.payload` contains the list. Optionally, you can also add `msg._uib.prop` set to `full` which will return an object where each key/value is the variable name. This can be usefull for some types of processing. ### `getWatchedVars()` - Get a list of all uibuilder watched variables :id=getWatchedVars Shows all variables that are being watched using `uibuilder.onChange()`. This function can also be called from Node-RED via `msg._uib.command` - `getWatchedVars`. The returned `msg.payload` contains the list. > [!WARNING] > `localStorage` is shared per _(sub)domain_, e.g. the IP address/name and port number. All pages from the same origin share the variables. ### `getStore(id)` - Attempt to get and re-hydrate a key value from browser localStorage :id=getStore Note that browser localStorage is persisted even after a browser closes. It can be manually cleared from the browser's settings. You can also remove an item using the `removeStore` function. If the `id` is not found in the store, `null` is returned. If the store is not available or some other error occurs, `undefined` is returned. All `id`s have a pre-defined UIBUILDER prefix added to the key name to help ensure that the key being saved will be unique. This prefix is defined in the library and cannot be changed, it is set to `uib_`. Because the browser storage API only allows strings as values, the data has to be serialised. This function attempts to unserialise (re-hydrate). It should be noted that sometimes, this process results in values that may differ from the original. For example, `uibuilder.setStore('mydate',new Date()); console.log( uibuilder.getStore('mydate') )` will return the saved date as an ISO8602 date string, not a JavaScript Date object. ### `removeStore(id)` - Attempt to remove a uibuilder key from browser localStorage :id=removeStore Does not return anything. Does not generate an error if the key does not exist. > [!WARNING] > `localStorage` is shared per _(sub)domain_, e.g. the IP address/name and port number. All pages from the same origin share the variables. It also only survives until the browser is closed. ### `set(prop, val, store, autoload)` - Set a uibuilder property and dispatch a change event :id=set This is the preferred method to set an exposed UIBUILDER variable or property. Do not try to set variables and properties directly. When using set, the variable that is set becomes ***responsive***. That is to say, that issuing a set triggers both the internal event handler (as used in `uibuilder.onChange('prop', ...)`) but also the DOM custom events `uibuilder:propertyChanged` and `uibuilder:propertyChanged:${propertyName}`. Normally, you will want to use the `onChange` handler for simplicity but the custom events also include additional data such as the previous value if needed . > [!TIP] > > To send an update message to Node-RED whenever a managed variable changes, use `uibuilder.onChange('varname', (data) => uibuilder.send({payload: data}))` in your JavaScript. Note that you can add additional custom data variables to the UIBUILDER object but care must be taken not to overwrite existing internal variables. This is useful if you want to be able to automatically process changes to your own variables using the `onChange` handler. #### Arguments * `prop` - (required) the variable to set and manage * `val` - (required) the value to set the prop to * `store` - (optional) true/false. Default=false. If true, the change is written to the browser `localStorage` as well if possible. > [!WARNING] > `localStorage` is shared per _(sub)domain_, e.g. the IP address/name and port number. All pages from the same origin share the variables. It also only survives until the browser is *closed*. * `autoload` - (optional) true/false. Default=false. If true and if `store` is true, uibuilder will try to automatically restore the variable on future page loads. Do another `set` with this set to false to turn off. #### Remote call This function *can also be called from Node-RED* via `msg._uib.command` - `set` with `msg._uib.prop` set to the variable name to set. and `msg._uib.value` set to the new value. `msg._uib.options` is used as `{store: true, autoload: true}` to optionally pass the additional arguments. #### Example ```javascript // Optionally send changes back to Node-RED uibuilder.onChange('loglevel', (data) => uibuilder.send({topic: 'loglevel changed', payload: data})) uibuilder.set('logLevel', 3) ``` ### `setStore(id, val, autoload)` - Attempt to save to the browsers localStorage :id=setStore Write a value to the given id to localStorage. Will fail if localStorage has been turned off or is full. The value to save has to be serialisable. Some JavaScript objects cannot be serialised (using `JSON.stringify`). If this happens `false` is returned and an error output to the browser console. However, you can store any basic value (number, string, boolean) as well as array's and objects. Browsers set a limit on the size of the store for a particular source. Typically this is 10MB but may be altered by the user. The user can turn off localStorage as well. Returns `true` if the save was successful, otherwise returns false. Errors are output to the browser console if saving fails but processing will continue. > [!WARNING] > `localStorage` is shared per _(sub)domain_, e.g. the IP address/name and port number. All pages from the same origin share the variables. It also only survives until the browser is closed. ## UI Handling These are the new dynamic, configuration-driven UI features. They let you create your UI dynamically from simple data sent to the client. In addition, internal message handling will recognise standard messages from node-red and process them. So these functions won't always be needed. You can also do `uibuilder.set('msg', {/*your object details*/})` which instructs the client to treat the object as though it had come from Node-RED. For functions with no descriptions, please refer to the code. In general, these will not need to be used in your own code. ### `applyTemplate(sourceId, targetId, config)` - Apply template element content to the page :id=applyTemplate This takes HTML (including styles and scripts) from a `<template>` tag. Templates are not active parts of the page but can be applied to a page using a script, potentially multiple times. Great if you have some standard content that you might need to dynamically add to the page (a new list entry or table row for example). `sourceId` (required) must be the HTML id attribute value of a `<template>` tag on-page. `targetId` (required) must be the id of an on-page element to which the template contents will be _appended_ as children. `config` (optional) is an object with 2 possible properties: * `config.onceOnly` (optional) if set to `true`, the contents of the template will be consumed and can not be re-used. Otherwise, the template may be reused as often as needed. * `config.attributes` (optional) if provided, must be an object containing attribute-name:value pairs. These will be applied to the **first child element only** of the template allowing any attribute (e.g. `id`, `class`, etc) to be set. Wrap your template content in a `<div>` or similar if you need attributes to be inherited by the rest of the template. * `config.mode` `{'insert'|'replace'|'wrap'}` Defines how to apply the template. *Default is 'insert'*. * `insert` (default) will *append* the template content to the end of any existing child elements of the target element. * `replace` will *replace* the target element's content with the template content. Any previous target content will be lost. * `wrap` will replace the target element with the template content but will also move the target element's previous content into the template's 1st `<slot>` if present. If no `<slot>` is present, the target previous content will be lost. > [!NOTE] > If the template contains `<style>` content, this becomes global to the page once it has been applied once. > > If the template contains `<script>` content, this is **run every time the template is applied**. So make sure you wrap any code in a test that will prevent it from re-running if that is important. Scripts are run in order of appearance and order of application. See the Node-RED example library "Dynamic SVG Example" flow for an example using this function. ### `addClass(classNames, el)` - Add one or more classes to an element :id=addClass `classNames` can be a string to add a single class or an array of strings for multiple classes. `el` must be a single HTML element, e.g. `$('#more')` ```javascript uibuilder.addClass('myclass', $('#more')) uibuilder.addClass(['class1', 'class2'], $('#more')) ``` See also [removeClass](#removeClass). Uses [`el.classList.add`](https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList/add) Note that if you want to _toggle_ a class on/off, use the HTML DOM: `$('#more').classList.toggle('myclass')`. See [MDN DOMTokenList/toggle](https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList/toggle) for details. ### `buildHtmlTable(data, options)` - Builds an HTML table from an array (or object) of objects :id=buildHtmlTable If the data is an object of objects, the outer keys are used as row ID's (prefixed with "r-"). If an `options.cols` array is not provided, the first row of data is also used to define the columns. The table HTML is returned as a string unless a parent id is provided. The options object can contain the following properties: * `cols` `{Array<columnDefinition>=}` - Column metadata. If not provided will be derived from 1st row of data * `parent` `{HTMLElement|string}` - If provided, the table will be added as a child instead of returned. May be an actual HTML element or a CSS Selector * `allowHTML` `{boolean=}` - Optional, default=false. If true, allows HTML cell content, otherwise only allows text. Always sanitise HTML inputs > [!NOTE] > Will issue a warning to the browser console if > 1000 rows of data are provided. This is because large tables can be slow to render. `columnDefinition` is a schema with the following properties: * `{number} index` The column index number * `{boolean} hasName` Whether the column has a defined name or not * `{string} title` The title of the column. Shown in the table header row * `{string=} name` Optional. A defined column name that will be added as the `data-col-name` to all cells in the column if defined * `{string|number=} key` Optional. A key value (currently unused) * `{"string"|"date"|"number"|"html"=} dataType` FOR FUTURE USE. Optional. What type of data will this column contain? * `{boolean=} editable` FOR FUTURE USE. Optional. Can cells in this column be edited? This allows the table columns to be defined in a more flexible way. ### `createTable(data, options)` - Add/replace HTML table to DOM :id=createTable `data` is an array of objects or an object of objects. Passed to `buildHtmlTable` to create the HTML. `options` is an object with the same properties as for `buildHtmlTable`. `options.parent` is automatically set to `body` if not provided. If an `options` object is provided but does not contain a `parent` property, an error is thrown. ### `convertMarkdown(mdText)` - Convert's Markdown text input to HTML :id=convertMarkdown Returns an HTML string converted from the Markdown input text. Only if the Markdown-IT library is loaded, otherwise just returns the input text. ### `elementExists(cssSelector, msg = true)` - Does the element exist on the page? :id=elementExists Returns a payload of true or false depending on whether the selected element exists on the page. Available as a command. Can be called from Node-RED with a message like: `{"command":"elementExists","prop":"#more"}`. ### **TEMPORARILY DEPRECATED** Do not use ~~`elementIsVisible(cssSelector, stop = false, threshold = 0.1)` - Can an HTML element currently be seen by the user?~~ :id=elementIsVisible > [!NOTE] > This function was not working correctly and the complexities of using the Intersection Observer API mean that a resolution would take more time than is currently warrented. > So, for now, this function is not usable. > > Info retained for future use. When the selected element is showing at least 10% in the users browser view, sends a message to Node-RED with `msg.isVisible` set to `true`. Will send another message if the elements shows less than 10%. Will continue to send messages when the element moves in and out of visibility. To turn it off, call it again with the same cssSelector but with the `stop` argument set to `true`. The `threshold` argument defaults to 0.1 (10%). It must be between 0 and 1 and represents, as a percentage, how much of the element must be in the browser viewport to trigger an output. Notes: * Unlike the visibility control message, this function sends a standard message AND is not effected by the browser tab visibility. So even if the tab containing the page is not visible but the element would be if the tab were showing, the result of this function is still TRUE. * Requires the browser to support the IntersectionObserver API (available to all mainstream browsers from early 2019). * The element has to exist on the page before it can be observed. * Turn on the optional `msg._uib` feature in the UIBUILDER node to see which client is sending the messages. * Due to the nature of the IntersectionObserver API, this fn is not available as a command for now. ### `getElementAttributes(el)` - Returns an object containing attribute-name/value keypairs (or an empty object) :id=getElementAttributes `el` = Target element. Attempts to get target elements attributes. This can fail for certain target types, if so, returns empty object. Ignores `class`, `id` and `name` attributes. Returns an array of key/value HTML attribute objects. ### `getElementClasses(el)` - Checks for CSS Classes and return as array if found or undefined if not :id=getElementClasses `el` = Target element. Checks for CSS Classes and returns them as an array if found or undefined if not. ### `getElementCustomProps(el)` - Returns an object containing an elements custom property/value pairs :id=getElementCustomProps An elements custom properties are those set using code that are not standard properties. Unlike an elements attributes, they can contain any valid JavaScript data. Excludes any properties that start with `_`. ### `getFormElementDetails(el)` Returns an object containing the key properties of a form element :id=getFormElementDetails For HTML Form elements (e.g. input, textarea, select), returns the key properties/attributes such as `value` and `checked`. The details are quite comprehensive and are designed to level out some of the inconsistencies normally found in HTML inputs. This function is also used by the `uibuilder.eventSend(event)` function. ### `getFormElementValue(el)` Check for el.value and el.checked, return as an object :id=getFormElementValue If found, `el.checked` will also set the `value` property in the returned object for ease of use. HTML only uses the `checked` property on 2 input types. Where it is used, the `value` property is not used. This inconsistency results in extra code or hard to find errors. This function therefore returns both properties where `checked` is used. ### `htmlSend()` - Sends the whole DOM/HTML back to Node-RED :id=htmlSend2 See under [Message Handling](#message-handling) above for details. ### `include(url, uiOptions)` - insert an external file into the web page :id=include Requires a browser supporting the [`fetch` API](https://caniuse.com/fetch). This function is asynchronous, that should be allowed for when using in custom front-end code. > [!NOTE] > This function uses the standard [`replaceSlot`](#replaceslotel-component-replace-or-add-an-html-element39s-slot-from-text-or-an-html-string) internal function. As such, it will use [DOMPurify](client-docs/readme#_1-dompurify-sanitises-html-to-ensure-safety-and-security) if loaded. DOMPurify will block the loading of most file types. The `uiOptions` parameter is **required** and must contain at least an `id` property. Options are: ```json { // REQUIRED: Must be unique on the web page and is applied to the wrapping `div` tag. "id": "unique99", // A CSS Selector that identifies the parent element to which the included // file will be attached. If not provided, 'body' will be used "parent": "#more", // Optional. If the parent has multiple children, identifies where the new element // will be inserted. Defaults to "last". May be "first", "last" or a number. "position": "last", // Optional. Attributes that will be applied to the wrapping DIV tag "attributes": { // NB: The "included" class is applied by default, if adding further // classes it is generally best to include that. "class": "myclass included" } // Other properties from the UI `replace` mode may also be included but // caution is required not to clash with properties from the included file. } ``` Each of the includes are wrapped in a `div` tag to which the supplied `id` attribute is applied along with a class of `included`. This makes styling of the included elements very easy. For example, to style an included image, add something like this to your `index.css` file: `.included img { width: 100%, border:5px solid silver;}`. The following file types are handled: * *HTML document/fragment* (*.html) - Will be wrapped in a div given the specified `id` attribute. If the `DOMPurify` library is loaded before the UIBUILDER client library, it will be used to sanitise the HTML. * *Image* - Any image file type recognised by the browser will be shown using an `img` tag (wrapped in the div as usual). * *Video* - Any video file type recognised by the browser will be shown using a `video` tag (wrapped in the div as usual). The video controls will be shown and it will auto-play if the browser allows it. * *JSON* - A `*.json` file will be syntax highlighted and shown in a panel. The syntax highlight CSS is contained in the `uib-brand.css` file and can be copied to your own CSS definitions if needed. The panel is defined as a `pre` tag with the `syntax-highlight` class added. * *PDF* - A `*.pdf` file will be shown in a resizable iFrame. * *Text* - The contents of the text file will be shown in a resizable iFrame. * *Other* - Any other file type will be shown in a resizable iFrame. Any file type that the browser cannot handle will trigger the browser to automatically download it. This is a browser limitation. Can be called from Node-RED with a message like: `{"command":"include","prop":"./test.html","value":{"id":"incHtm","parent":"#more","attributes":{"style":"width:50%;"}}}`. ### `loadScriptSrc(url)`, `loadStyleSrc(url)`, `loadScriptTxt(string)`, `loadStyleTxt(string)` - Attach a new script or CSS stylesheet to the end of HEAD synchronously :id=load Either from a remote URL or from a text string. Directly call the functions of the same name from the `ui.js` library. ### `loadui(url)` - Load a dynamic UI from a JSON web response :id=loadui Requires a valid URL that returns correct _ui data. For example, a JSON file delivered via static web server or a dynamic API that returns JSON as the body response. Directly calls `_ui.loadui` from the `ui.js` library. ### `notify(config)` - Use the browser and OS notification API to show a message to the user :id=notify Requires a browser that supports the [Notification API](https://developer.mozilla.org/en-US/docs/Web/API/Notification/Notification). > [!WARNING] > Users will almost certainly have to spot that their browser initially blocks notifications for a particular web endpoint. > They will need to allow notifications manually before they will work. #### Examples ```javascript // Simple string input uibuilder.notify( 'My simple message to the user' ) // Node-RED msg object style input uibuilder.notify( {topic: 'My Title', payload: 'My simple message to the user'} ) // Notification API style input uibuilder.notify( {title: 'My Title', body: 'My simple message to the user'} ) // If notifyConfig.return = true, a promise is returned. // The resolved promise is only returned if the notification is clicked by the user. // Can be used to send the response back to Node-RED uibuilder.notify(notifyConfig).then( res => uibuilder.eventSend(res) ) ``` When returning the response to Node-RED, the following style of msg is output: ```json { "payload":"notification-click", "_ui": { "type":"eventSend", "props": {"userAction":"click"}, "notification": { "actions":[],"badge":"","body":"My simple message to the user", "data":null,"dir":"auto","icon":"","image":"","lang":"", "renotify":false,"requireInteraction":false,"silent":false, "tag":"","timestamp":1688303511972,"title":"My Title","vibrate":[] }, "event":"click", "clientId":"-4MmSwnSrvJIOzVWgqcQv","pageName":"index.html","tabId":"t588851" }, "_socketId":"RlLfI6HDnhbcttB5AAAH","topic":"title","_msgid":"64a614702ff90fff" } ``` ### `removeClass(classNames, el)` - Remove all, one or more classes from an element :id=removeClass `classNames` can be a string to remove a single class, an array of strings for multiple classes. Or can be an empty string, `null` or `undefined` to remove all classes (will remove the `class` attribute). `el` must be a single HTML element, e.g. `$('#more')` ```javascript uibuilder.removeClass(null, $('#more')) // remove all uibuilder.removeClass('myclass', $('#more')) // remove 1 uibuilder.removeClass(['class1', 'class2'], $('#more')) // remove multiple ``` See also [addClass](#addClass). Uses [`el.classList.remove`](https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList/remove) Note that if you want to _toggle_ a class on/off, use the HTML DOM: `$('#more').classList.toggle('myclass')`. See [MDN DOMTokenList/toggle](https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList/toggle) for details. ### `replaceSlot(el, component)` - Replace or add an HTML element's slot from text or an HTML string :id=replaceSlot This function is mostly for internal use. `el` must be an HTML Element, its slot content will be replaced. `component` must be a single `_ui` components entry with a `slot` property that will be used to replace the `el`s slot. Will use [DOMPurify](client-docs/readme#_1-dompurify-sanitises-html-to-ensure-safety-and-security) if that library has been loaded. Directly calls `_ui.replaceSlot` from the `ui.js` library. ### `replaceSlotMarkdown(el, component)` - Replace or add an HTML element's slot from a Markdown string :id=replaceSlotMarkdown This function is mostly for internal use. The [Markdown-IT](client-docs/readme#_2-markdown-it-converts-markdown-markup-into-html) library must be loaded for this to work, otherwise it silently fails. `el` must be an HTML Element, its slot content will be replaced. `component` must be a single `_ui` components entry with a `markdownSlot` property that will be used to replace the `el`s slot after being run through Markdown-IT to turn it into HTML. Will use [DOMPurify](client-docs/readme#_1-dompurify-sanitises-html-to-ensure-safety-and-security) if that library has been loaded. Directly calls `_ui.replaceSlotMarkdown` from the `ui.js` library. ### `returnElementId(el)` - Return elements existing ID or, add a new unique id to the element and return it :id=returnElementId Returns the element's existing ID. Or, if not present, attempts to create a page unique id from the name attribute or the attribute type. The last 2 will use a page-unique number to enforce uniqueness. ### `sanitiseHTML(htmlText)` - Ensures that input HTML text is safe :id=sanitiseHTML Returns a safe, sanitised HTML string IF the DOMPurify library is loaded. Otherwise returns the input. ### `scrollTo(cssSelector)` - Scroll visible page to an element based on a CSS Selector :id=scrollTo If the top of the selected element is not currently visible in the browser window, will scroll (the top by default) to make it visible. An optional 2nd argument can be added to better control what becomes visible. Please see the options for the [`scrollIntoView`](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView) DOM API for details of the options. The options object is passed through to that API. In addition, the cssSelector string `top` or `start` will scroll to the top of the page and `bottom` or `end` will scroll to the bottom of the page. Returns `true` if the element was found, false otherwise. Can also be called from Node-RED with a command message: `{"_uib": {command: "scrollTo", "prop": "cssSelector"}}` or, with options: `{"_uib": {command: "scrollTo", "prop": "#mydivid", "value": {"block": "bottom"}}}`. ### `showDialog(type, ui, msg)` - Show a toast or alert style message on the UI :id=showDialog Directly calls `_ui.showDialog` from the `ui.js` library. `type` is either "notify" or "alert". `msg` is optional. If passed, msg.topic and msg.payload will be used. `ui` allows control over the element that is created. Allowed properties are: ```json { // Optional title. May include HTML. If not set, will use msg.topic "title": "", // The message content. Note that msg.payload will also be added. May include HTML. "content": "", // Optionally set to any class name or one of: // 'primary', 'secondary', 'success', 'info', 'warn', 'warning', 'failure', 'error', 'danger' "varient": "", // Optionally turn off the 10s auto-hide of the message. "noAutohide": false, "autohide": true, // opposite of noAutoHide, use one or the other "autoHideDelay": 10000, // 10s // Make the dialog modal - currently not used as all dialogs are model. For future use. "modal": true, // Append messages after each other otherwise latest messages appear on top. "appendToast": false, } ``` ### `showMsg(boolean, parent=body)` - Show/hide a card that automatically updates and shows the last incoming msg from Node-RED :id=showMsg Simply add `uibuilder.showMsg(true)` early in your index.js custom code and a box will be added to the end of your page that will automatically show the last message sent from Node-RED. Use `uibuilder.showMsg()` to toggle the display. `uibuilder.showMsg(false)` or `uibuilder.showMsg()` will remove the box and stop the updates. You can also position the box in a different location by specifying a "parent". This is a CSS selector that, if found on the page, UIBUILDER will add the box to the end of. For example, `uibuilder.showMsg(true, 'h1')` would attach the box to the end of a heading level 1 element on the page. Don't forget that the box will inherit at least some of the CSS style from the parent, so attaching to an H1 element will make the text much bigger. This function can also be called from Node-RED via `msg._uib.command` - `showMsg` with `msg._uib.value` set to `true`. Leave the value property off to toggle the display. Adds/removes `<div id="uib_last_msg">` to/from the page. ### `showStatus(boolean, parent=body)` - Show/hide a card shows the current status of the uibuilder client library :id=showStatus Simply add `uibuilder.showStatus(true)` early in your index.js custom code and a box will be added to the end of your page that will show all of the important settings in the UIBUILDER client. Use `uibuilder.showStatus()` to toggle the display. `uibuilder.showStatus(false)` or `uibuilder.showStatus()` will remove the box and stop the updates. You can also position the box in a different location by specifying a "parent". This is a CSS selector that, if found on the page, UIBUILDER will add the box to the end of. For example, `uibuilder.showStatus(true, 'h1')` would attach the box to the end of a heading level 1 element on the page. Don't forget that the box will inherit at least some of the CSS style from the parent, so attaching to an H1 element will make the text much bigger. This function can also be called from Node-RED via `msg._uib.command` - `showStatus` optionally with `msg._uib.value` set to `true`. Leave the value property off to toggle the display. Adds/removes `<div id="uib_status">` to/from the page. ### `syntaxHighlight(json)` - Takes a JavaScript object (or JSON) and outputs as formatted HTML :id=syntaxHighlight Is used internally by the `showMsg` function but may be useful for custom processing. If used in custom code, make sure to wrap the output in a `<pre>` tag. Requires some CSS that is contained in both the `uib-brand.css` and older `uib-styles.css`. Feel free to copy to your own CSS if you don't want to reference those files. Use as: ```javascript const eMsg = $('#msg') // or document.getElementById('msg') if you prefer if (eMsg) eMsg.innerHTML = uibuilder.syntaxHighlight(msg) ``` ### `tblAddRow(tbl, rowData, options)` - Add or replace a row of data in a table :id=tblAddRow Adds or replaces a single row in an existing table>tbody * `tbl` is either a CSS Selector for the table or a reference to the HTML Table Element. * `rowData` is single row of column/cell data either as an array or an object. * `options` is an object with the following properties: * `{number=} body` Optional, default=0. The tbody section to add the row to. * `{boolean=} allowHTML` Optional, default=false. If true, allows HTML cell content, otherwise only allows text. Always sanitise HTML inputs * `{string=} rowId` Optional. HTML element ID for the added row * `{number=} afterRow` Optional. If provided, the new row will be added after this row number * `{number=} beforeRow` Optional. If provided, the new row will be added before this row number. Ignored if afterRow is provided * `{number=} replaceRow` Optional. If provided, the specified row will be REPLACED instead of added. Ignored if afterRow or beforeRow is provided * `{Array<columnDefinition>=} cols` Optional. Data about each column. If not provided, will be calculated from the table A DOM element reference to the newly added row is returned to enable further changes to be made if required. > [!TIP] > Use the `rowIndex` DOM property for the row number if needed since the row number may not be the same as the array index and may change if rows are added or removed. ### `tblAddListener` - Add an event listener to a table row or cell :id=tblAddListener Add table event listener that returns the text or html content of either the full row or a single cell as required. > [!NOTE] > Assumes that the table has a `tbody` element. If cells have a `data-col-name` attribute, it will be used in the output as the column name. #### Examples ```javascript // Update myVar with the full row data when any cell on a row is clicked. Also outputs to Node-RED. tblAddListener('#eltest-tbl-table', {}, myVar) // Update myVar2 with the cell data when any cell is clicked. Also outputs to Node-RED. tblAddListener('#eltest-tbl-table', {eventScope: 'cell'}, myVar2) ``` #### Arguments ```javascript /** * @param {string} tblSelector The table CSS Selector * @param {object} [options={}] Additional options * @param {"row"|"cell"=} options.eventScope Optional, default=row. Return data for either the whole row (as an object) or for the single cell clicked * @param {"text"|"html"=} options.returnType Optional, default=text. Return text or html data * @param {number=} options.pad Optional, default=3. Will be used to front-pad unnamed column references with zeros. e.g. 3 => "C002"/"C012"/"C342" * @param {boolean=} options.send Optional, default=true. If uibuilder is present, will automatically send a message back to Node-RED. * @param {string|number=} options.logLevel Optional, default=3/info. Numeric or string log level matching uibuilder's log levels. * @param {string} [options.eventType] Optional, default=click. What event to listen for. * @param {object=} out A variable reference that will be updated with the output data upon a click event */ ``` ### `tblRemoveRow(tbl, rowId, options)` - Remove a row from a table :id=tblRemoveRow Removes a row from an existing table `tbl` is either a CSS Selector for the table or a reference to the HTML Table Element. `rowIndex` is the row number to remove (1st row is 0, last row is -1). `options` is an object with the following properties: * `{number=} body` Optional, default=0. The tbody section to add the row to. ### `ui(json)` - Directly manage UI via JSON :id=ui Takes either an object containing `{_ui: {}}` or simply simple `{}` containing ui instructions. See [Config Driven UI](client-docs/config-driven-ui.md) for details of the required data. Directly calls `_ui.ui` from the `ui.js` library. ### `uiGet(cssSelector, propName=null)` - Get most useful information, or specific property from a DOM element :id=uiGet Will return an array of found elements with properties. If no `propName` supplied, will return a selection of the most useful information about the selected element(s). Returned data can be sent back to Node-RED using: `uibuilder.send( uibuilder.uiGet('#myelementid') )`. Where a propName is supplied, if you ask for the `value` attribute - `uibuilder.uiGet("#eltest", "value"}` - if the selected element is an `input` type, the input's value attribute will be returned. But if it is some other kind of element type, the element's inner text will be returned. Can be called from Node-RED with a message like: `{"_uib: {"command": "uiGet", "prop": "#more"} }`. Uses `nodeGet` internally. ### `uiWatch(cssSelector, startStop=true/false/'toggle', send=true, showLog=true)` - watches for any changes to the selected HTML elements :id=uiWatch Uses [Mutation Observer](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver/MutationObserver) to watch for and report on any changes to the DOM (the page). Can send the output to the console log (the `showLog` argument. Shows at log level 2 - Info) and/or back to Node-RED (the `send` argument). Uses `nodeGet` (the same as `uiGet`) to capture standard information about added/removed nodes. Sends useful data back to Node-RED automatically. It also triggers a custom event (`uibuilder:domChange`) to allow front-end processing too. If `startStop` is undefined, null or 'toggle', the watch will be toggled. Can be called from Node-RED with a message like: `{"_uib: {"command": "uiWatch", "prop": "#more"} }`. ## HTML/DOM Cacheing ### `clearHtmlCache()` - Clears the HTML previously saved to the browser localStorage :id=clearHtmlCache ### `restoreHtmlFromCache()` - Swaps the currently displayed HTML to the version last saved in the browser localStorage :id=restoreHtmlFromCache ### `saveHtmlCache()` - Manually saves the currently displayed HTML to the browser localStorage :id=saveHtmlCache > [!NOTE] > Browser local cache is generally limited to 10MB for the whole source domain. > Therefore, it is quite easy to exceed this - use with caution. ### `watchDom(startStop)` - Start/stop watching for DOM changes. Changes automatically saved to browser localStorage :id=watchDom `uibuilder.watchDom(true)` will start the browser watching for any changes to the displayed HTML. When it detects a change, it automatically saves the new HTML (the whole page) to the browser's `localStorage`. This persists across browser and device restarts. You can ensure that the page display looks exactly like the last update upon page load simply by adding `uibuilder.restoreHtmlFromCache()` at the start of your index.js custom code. > [!note] > Browser `localStorage` capacity is set by the browser, not UIBUILDER. Very large pages might concevably fill the storage as might other things saved to it. > > You should be able to change the capacity in the browser settings but of course, this would have to be done on every client device. ## Event Handling > [!NOTE] > You can use one or more of the `msg._uib.pageName`, `msg._uib.clientId`, or `msg._uib.tabId` properties > to control whether a specific page, client or browser tab will process an inbound message. > Use this where you have multiple pages or clients and need to target a message to a specific one. ### `onChange(prop, callbackFn)` - Register on-change event listeners for uibuilder tracked properties :id=onChange Returns a reference to the callback so that it can be cancelled if needed. Uses the `uibuilder:propertyChanged` event internally. #### Example ```javascript const msgChgEvt = uibuilder.onChange('msg', (msg) => { // Dump the msg as text to the html element with an id of "msg" const eMsg = $('#msg') if (eMsg) eMsg.innerHTML = uibuilder.syntaxHighlight(msg) }) ``` ### `cancelChange(prop, cbRef)` - remove all the onChange li