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.
394 lines (262 loc) • 20.8 kB
Markdown
---
title: Custom web components
description: |
Web components - AKA "Widgets" - built into the UIBUILDER client and information about external web components.
created: 2023-10-08 13:44:56
updated: 2025-01-06 18:05:18
---
The following custom web components are built into UIBUILDER:
* [`<apply-template>`](#apply-template) - Apply the content of an HTML `<template>` to the UI without the need for JavaScript.
* [`<uib-meta>`](#uib-meta) - Display page metadata such as the created/last-updated timestamp or size. Taken from the physical page file.
* [`<uib-var>`](#uib-var) - Substitute dynamic data into the UI (similar to `{{varname}}` in frameworks).
## Introduction
UIBUILDER can work with front-end frameworks such as REACT, VueJS, etc. However, it does not need them. But one thing that these frameworks often have are collections of components that you can add to your HTML. Each component will produce something useful in your web page such as an information card, tabbed interface, navigation menu, etc.
For more modern browsers though, *there is an alternative to a framework and that is "[web components](https://developer.mozilla.org/en-US/docs/Web/API/Web_Components)"*. These are a W3C international standard and they are defined using JavaScript. The built-in components described here are examples of such components.
To make use of a web component, all that is needed is to load it as a library. Web components are written as ES Modules (ESM). In this form, you can only use them with the ESM version of the uibuilder client library and they will need loading as an `import` statement in your module `index.js` just like the client library itself. (`uib-var` & `uib-meta` are loaded for you though, you don't have to do anything).
However, some components may also be built so that they can be loaded as a linked script. This is easily done using a build tool such as [`esbuild`](https://esbuild.github.io/). When built this way, they can be used with the standard IIFE version of the uibuilder client library. A well-crafted component will often include a version for loading this way but it is up to the author to make this available. Check the component's documentation for details.
Needless to say, any web components written by [TotallyInformation](https://github.com/TotallyInformation) and certainly any included with UIBUILDER will be written such that their library can simply be linked with no other code needed to make them work. However, they will also be usable as standard ES modules. Again though, the built-in components are loaded for you automatically by the uibuilder client library, you don't load them yourself.
> [!NOTE]
> UIBUILDER's no-code `uib-element` node currently sends out low-code JSON data that describes each element, the uibuilder client library converts (hydrates) this configuration into HTML and inserts to or updates the page. While this is reasonably efficient since no actual HTML/JavaScript code is sent, it could be even more efficient by having a corresponding *web component* for each element. This is something that is likely to happen in a future release.
> [!TIP]
> There is a more extensive package of web components available from [Totally Information](https://wc.totallyinformation.net). They are designed to work with or without UIBUILDER. Indeed they should work with other web servers and also with the Node-RED Dashboard or http-in/-response nodes.
>
> The built-in components described here are built to the same standards used in the other package.
> [!TIP]
> The web components described on this page **do not need to be separately loaded** in your own front-end code. They are already included in the uibuilder client library. You can simply use them in your HTML.
## Built-in: `<apply-template>` :id=apply-template
This small but powerful component allows you to keep "templates" in your HTML that are copied to another place. The referenced template's content will _replace_ to the contents of the `<apply-template>` tag.
> [!TIP]
> If you have a `<slot></slot>` tag in your template, the original HTML contents of the `<apply-template>` tag will be moved into the slot.
I've never understood why this isn't a standard HTML feature! Doing this by hand requires some significant knowledge. As always, UIBUILDER is here to make life easier but it still uses "just" standard HTML, CSS and JavaScript.
> [!NOTE]
> The uibuilder front-end library also has a built-in function [`uibuilder.applyTemplate((sourceId, targetId, config)`](client-docs/functions#applyTemplate). It does the same thing as this component but does it in JavaScript code so you can do `uibuilder.applyTemplate('template-1', 'more')` in code to get a similar effect. The main differences being that: You must supply the id of an element to which the template contents will be appended. And you can apply attributes to the _1st child element_ using the `config` parameter.
### Attributes :id=apply-template-attribs
#### template-id
This attribute is required. The value must be the HTML id attribute value of the required `<template id="xxxx">....</template>` tag ("xxxx" in this example).
This defines the source template. The contents of which will be copied ("applied") to replace the content of the `<apply-template>` tag.
#### once
If this attribute is present (it does not need a value), the source template will be emptied and cannot be re-used.
This is helpful if you need to ensure that the source template can only ever be applied once.
Technically, the content of the template is ["adopted"](https://developer.mozilla.org/en-US/docs/Web/API/Document/adoptNode) and so is not longer available in the template.
### Example :id=apply-template-examples
```html
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="../uibuilder/images/node-blue.ico">
<title>Apply-Template example - Node-RED uibuilder</title>
<meta name="description" content="Node-RED uibuilder - Apply-Template example">
<link type="text/css" rel="stylesheet" href="./index.css" media="all">
<script defer src="../uibuilder/uibuilder.iife.min.js"></script>
<!-- Define some templates -->
<template id="template-1">
<!-- Templates are not "live" on a page. Styles and scripts
are not active. The content does not show. -->
<style>
/* styles become global once applied */
.t1-green {
color:chartreuse
}
</style>
<h1 class="t1-green">Template 1</h1>
<slot><!-- existing content in the <apply-template> tag will appear here when applied --></slot>
<p class="warn">This is the content of template 1.</p>
<script>
/** Scripts are run in order of application of the template
* to the live page. They are run EVERY TIME the template
* is applied so may run more than once. */
console.log('template-1')
if (!window['template1']) {
/** This will only ever run once */
const myvar = 1
window['template1'] = true
}
</script>
</template>
</head><body>
<h1 class="with-subtitle">Apply-Template Example</h1>
<div role="doc-subtitle">Using the uibuilder IIFE library.</div>
<div id="more">
<apply-template template-id="template-1">
<p style="background-color:darkred;">Something interesting within - this text will reappear in the slot position from the template.</p>
</apply-template>
<apply-template template-id="template-1">
<p>
... Yes, you can apply a template >1 time as long as
you haven't used the "once" attribute...
</p>
</apply-template>
</div>
</body></html>
```
## Built-in: `<uib-meta>` :id=uib-meta
Display metadata information about the containing page.
This web component will output page data such as the created/last-updated dates and the page size. The outputs have some simple formatting options available.
The uibuilder client library will automatically request this data from the uibuilder node which gets the data from the server's filing system.
> [!NOTE]
> This component could easily be extended to get things like the uibuilder node's title and description and other information if required.
### Attributes :id=uib-meta-attribs
#### type :id=uib-meta-type
* `created` - the date/time the page's static file was created. This is the default output if `type` is not specified.
* `updated` (AKA `modified`) - the last date/time the page's static file was updated.
* `both` (or `created-updated` or `crup`) - Both the created and updated date/times.
* `size` - The size of the page's static file in bytes.
* `all` - All of the above.
> [!NOTE]
> The data is from the static file and so does not include updates/sizes from dynamic updates.
> This makes it less useful for highly dynamic, data-driven pages. It is more useful for static wiki/blog/news style pages.
#### format :id=uib-meta-format
For date/time output:
* `d` - Date only.
* `dt` - Date and time.
* `t` - Time only.
Date and time values use the browser's locale specific outputs.
For size output:
* `k` - kilobytes (_bytes/1024_) to 1 decimal place.
* `m` - megabytes (_bytes/(1024*1024)_) to 2 decimal places.
### Examples :id=uib-meta-examples
```html
<div id="more">
<uib-meta type="created" format="t"></uib-meta><!-- Created time only --><br>
<uib-meta format="d"></uib-meta><!-- default: created. Date only --><br>
<uib-meta type="updated" format="dt"></uib-meta><br>
<uib-meta type="both" format="d"></uib-meta><br>
<uib-meta type="size" format="k"></uib-meta><br>
</div>
```
## Built-in: `<uib-var>` :id=uib-var
Include easily updated output on a web page.
This component provides another way to make it easy to include dynamic data in your web UI and to update it. It is roughly equivalent to the "mustache" style brackets found in many front-end frameworks (<code>{{myvariable}}</code>). However, unlike those, `<uib-var>` is 100% standard HTML. The component reference is replaced in the web page by a uibuilder managed variable value, msg topic payload, or the returned value of a filter function - depending on the attributes provided.
Included in your page using the `<uib-var>`, see the examples below on how to include this in your HTML. You can also use the no-code `uib-tag` node to add it to your HTML.
There is no need to separately load the component, that is done automatically by the uibuilder client library.
### Attributes :id=uib-var-attribs
These attributes can be added to the `<uib-var>` tag. Noting that attributes must always have _string_ values, e.g. `topic="my/topic/#1"`.
#### topic :id=uib-var-topic
Adds a background "listener" for messages from Node-RED via UIBUILDER that have the `topic` property matching this attribute. For example, the HTML `<uib-var topic="my/topic/#1"></uib-var>` will be automatically updated to show `42` when the corresponding uibuilder node is sent a message containing `{topic: 'my/topic/#1', payload: 42}`.
This is the easiest way to use the `uib-var` component with UIBUILDER.
This approach is especially useful to be able to display MQTT topic values in your UI
> Cannot be used in conjunction with the `variable` attribute.
#### variable :id=uib-var-variable
Displays the *value* of the given variable name in your web page and *dynamically updates* as long as the variable is changed using `uibuilder.set()`; or from Node-RED using the appropriate uib set command, e.g. `msg = {"command":"set","prop":"myVar","value":"42"}`. By default, the tag inserts the variable value inline with other text. Class and Style attributes can be added as for any other HTML.
> Cannot be used in conjunction with the `topic` attribute.
#### filter :id=uib-var-filter
Takes a string representation of a function to run before display. It can be used on its own or in conjunction with either the `variable` or `topic` attributes.
When used with the `variable` or `topic` attributes, the function will be passed the corresponding value as its first parameter. Any additional parameters provided on this attribute will be merged _after_ the value.
The function must be accessible from the global `window` environment and return valid text or HTML which will be used as the displayed content.
The attribute string may include simple arguments only (e.g. strings, booleans and numbers - `filter="myfunc('text', 42, true)"`).
Dotted notation for the function name is allowed (e.g. `uibuilder.get('version')`) but the array style (e.g. `uibuilder['get']('version')`) is not permitted.
If the function name is not found in the global window context, the `uibuilder` context is searched so `get('version')` will call `uibuilder.get('version')` so long as `get` is not defined as `window.get` which would take preference.
Generally, it will be best to define your own function. Further standard filters may be added in future releases of UIBUILDER. Until then, a list of standard filters included in the uibuilder is provided [lower-down](#useful-filter-functions).
> [!NOTE]
> If having problems getting your filter functions recognised, try adding them earlier in the HTML.
>
> Notably, **before** the uibuilder client library is loaded.
>
> For example, defining the `lang` filter:
> ```html
<!-- #region Supporting Scripts. These MUST be in the right order. Note no leading / -->
<script>
// Some filter functions
globalThis.lang = () => navigator.language
globalThis.yen = (v) => uibuilder.formatNumber( v, 2, 'ja-JP', { style: 'currency', currency: 'JPY' } )
</script>
<script defer src="../uibuilder/uibuilder.iife.min.js"></script>
<script defer src="./index.js"></script>
<!-- #endregion -->
> ```
#### undefined :id=uib-var-undefined
If present or set to `on`, or `true`, the component will show even if the variable is undefined.
If not present or set to anything else, an undefined variable will show the _slot content_ (anything put between the open and close tag). This lets you have an initial value showing that is replaced as soon as the variable is actually set.
#### report :id=uib-var-report
If present or set to `on`, or `true`, the component will return a standard message back to Node-RED when the variable changes value.
#### type :id=uib-var-type
Must be one of 'plain', 'html', 'markdown', or 'object'. `plain` and `html` simply insert the variable as-is. `markdown` allows the insertion of Markdown as long as the Markdown-IT library is loaded (it will also sanitise the resulting HTML if the DOMPurify library is loaded). `object` does some basic formatting to allow object or array variables to be more easily read.
This works with Markdown via the `uib-element` node as well and even works if DOMPurify is loaded as overrides to its filters are provided.
### Styling
The component tries to load `./index.css` as a stylesheet so that your own styling can be used in any output.
Simply ensure that the file is served from the same URL location as your main page.
This is the standard method for UIBUILDER instances but this feature is not dependent on UIBUILDER.
### Useful filter functions
#### formatNumber :id=uib-var-format-number
Formats a number to a given locale and optionally, a set number of decimal places.
Example, formatting to 2dp using the Japanese locale:
```html
<uib-var topic="mynumber" filter="uibuilder.formatNumber(2, 'ja-JP')"></uib-var>
```
Note that the value is passed automatically so the first provided argument is the number of decimal places.
Parameters: `formatNumber(value, decimalPlaces, intl, opts)`. Where `opts` is an INTL formatting object.
See details in the [client functions doc](client-docs/functions#formatNumber).
#### Other pre-defined filters
Check out the [uibuilder client library functions documentation](client-docs/functions) which contains other functions that may be of use.
### Examples :id=uib-var-examples
#### Show the uibuilder client library version
```html
<p>
UIBUILDER client library version
"<uib-var variable="version"></uib-var>".
</p>
```
#### Show the last message send/received to/from Node-RED
```html
<p>Last msg sent: "<uib-var variable="sentMsg" type="object"></uib-var>".</p>
<p>Last msg received: "<uib-var variable="msg" type="object"></uib-var>".</p>
```
#### Show undefined variable and report to Node-RED when variable changes
```html
<!-- Shows the value even if undefined. Sends a msg to Node-RED when myVar is changed -->
<p>The answer is <code><uib-var variable="myVar" undefined report></uib-var></code>.</p>
```
#### Using in Markdown content
```markdown
## My Heading
The version of the uibuilder client library currently
loaded is `<uib-var variable="version"></uib-var>`.
```
#### Adding a `uib-var` tag to your page using no-code from Node-RED
Use a `uib-tag` node:

## External components
Web components can be challenging to build but are often fairly simple. There are plenty of web resources to get you started with development of them. However, there are also a lot of existing components that you can easily make use of with Node-RED and UIBUILDER.
Such components can be manually added to your `index.html` file OR they can be dynamically added using UIBUILDER's no-code `uib-tag` node.
Some potentially useful components are shown below.
### Totally Information experimental web components
Totally Information has a set of web components that work with or without UIBUILDER though they will generally have extra features when used with.
The components have [their own website](https://wc.totallyinformation.net) that shows demos, documentation and tests.
The components have various levels of readiness. Some are ready to be used now and some are marked as "Alpha" quality and should only be used for testing.
See the [TotallyInformation/web-components GitHub repo](https://github.com/TotallyInformation/web-components) for details.
### HotNiPi Gauge component
A nice looking gauge was gifted to the community by Node-RED forum contributor HotNiPi and this also works with or without UIBUILDER. See [HotNipi Gauge Web Component](https://github.com/TotallyInformation/gauge-hotnipi) for details. In line with UIBUILDER standards, the gauge component is available in both ESM and IIFE formats for ease of use.
### Include-fragment-element
This web component is written and maintained by GitHub. It lets you have something like this in your
```html
<div class="tip">
<include-fragment src="./tips/tip1.html">
<p>Loading tip…</p>
</include-fragment>
</div>
```
Where `<include-fragment src="/tips">...</include-fragment>` is completely replaced by the content of the loaded HTML fragment.
HTML Fragments in this case are files of HTML content without any of the `<html>`, `<head>`, `<body>` and other structural HTML, just raw content.
Install locally using the UIBUILDER Library Manager with the name `@github/include-fragment-element` and then include in your index.html file as:
```html
<script defer src="../uibuilder/vendor/@github/include-fragment-element/dist/index.min.js"></script>
```
Or use direct from the Internet with:
```html
<script defer src="https://cdn.jsdelivr.net/npm/@github/include-fragment-element/dist/index.min.js"></script>
```
ESM loads are also available. [See GitHub for details](https://github.com/github/include-fragment-element/).
### Other web components and component libraries
There are many web components and web component libraries available. All should work with UIBUILDER.
- [Basic Material Design components](https://material-web.dev/about/intro/) - supported by Google designers and developers.
- [Elix components](https://component.kitchen/elix) - high-quality basic elements and composable mixins.
- [Webcomponents.org](https://www.webcomponents.org/) - elements, collections and how-to's.
- [Webcomponents.org libraries](https://www.webcomponents.org/libraries)
- [GitHub Components](https://github.com/orgs/github/repositories?q=component) - various components and tools written by GitHub devs.
- [Mozilla Developer Network (MDN) example components](https://github.com/mdn/web-components-examples) - example components, useful to get started on your own.
- [Open Web Components](https://open-wc.org/) - guides, tools and libraries for developing web components.
- [Open Web Components community libraries](https://open-wc.org/guides/community/component-libraries/) - list of potentially useful component libraries.
- "Awesome" lists
- [obetomuniz/awesome-webcomponents](https://github.com/obetomuniz/awesome-webcomponents/blob/master/README.md)
- [web-padawan/awesome-web-components](https://github.com/web-padawan/awesome-web-components/blob/main/README.md)