-
-
Notifications
You must be signed in to change notification settings - Fork 90
Example: MoonJS
Here is a quick example using UIbuilder which is an alternative UI for Node-RED.
It demonstrates the use of a lightweight front-end library, MoonJS, which can be a lot faster and easier to work with than Node-RED's built-in Dashboard UI once you reach the point of needing to customise the UI beyond the standard capabilities of Dashboard.
This is a really quick demo of what you can do with uibuilder. MoonJS is a lightweight alternative to the likes of Angular (used by Dashboard), REACT, VueJS, etc. It is fast and really easy to use.
Here are the steps to set up (details given below):
- Install MoonJS & uibuilder using npm
- Add reference for MoonJS to settings.js
- Restart Node-RED and add a uibuilder node with input and output nodes (see the flow for the simplest of examples)
- Copy the template html, js and css files from the uibuilder master folder to the uibuilder folder in your userDir. Amend as per the examples below
- Open the uibuilder URL and send msg's both ways to see it working.
Note that I will assume your userDir
is at ~/.node-red
which is the default.
Open a terminal/command prompt, cd to ~/.node-red
(most platforms including Windows PowerShell, %USERPROFILE%\.node-red
for Windows cmd prompt).
npm install node-red-contrib-uibuilder moonjs --save
Add the following to your ~/.node-red/settings.js
file:
uibuilder: {
userVendorPackages: ['moonjs'],
debug: true
}
Don't forget to fix any preceding/trailing comma's to ensure that the JavaScript object is valid.
After restarting Node-RED, import the flow given below and deploy. Note that the instance of the uibuilder
node in the example flow has its URL set to moon
.
You should now have a folder ~/.node-red/uibuilder/moon/src
. You need to copy over the files
shown below into that folder. You will be replacing the files that the deployment of the node has copied over for you.
In your favourite browser, navigate to the moon url. If you are using default settings and running Node-RED on the same machine as the browser, this will be http://localhost:1880/moon.
Now you can use the button in the web page to send a message back to Node-RED, the data will appear in the debug output pane. Then you can use the Inject node to send a message to the browser.
Import this to Node-RED & deploy.
[
{
"id": "65c207ab.b53e08",
"type": "debug",
"z": "106ba95c.ff91e7",
"name": "moonjs-debug",
"active": true,
"console": "false",
"complete": "true",
"x": 480,
"y": 260,
"wires": []
},
{
"id": "2283ab5e.70d5a4",
"type": "uibuilder",
"z": "106ba95c.ff91e7",
"name": "moonjs",
"url": "moon",
"fwdInMessages": false,
"customFoldersReqd": true,
"x": 270,
"y": 260,
"wires": [
[
"65c207ab.b53e08"
]
]
},
{
"id": "eb50e622.116878",
"type": "inject",
"z": "106ba95c.ff91e7",
"name": "",
"topic": "uibuilder-moon",
"payload": "{\"lib\":\"moon\", \"testNum\": 500}",
"payloadType": "json",
"repeat": "",
"crontab": "",
"once": false,
"x": 130,
"y": 260,
"wires": [
[
"2283ab5e.70d5a4"
]
]
}
]
These go in ~/.node-red/uibuilder/moon/src
<!doctype html>
<html lang="en">
<!--
This is the default, template html for uibuilder.
It is only meant to demonstrate the use of JQuery to dynamically
update the ui based on incoming/outgoing messages from/to the
Node-RED server.
You will want to alter this to suite your own needs. To do so,
copy this file to <userDir>/uibuilder/<url>/src.
-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">
<title>Node-RED UI Builder</title>
<meta name="description" content="Node-RED UI Builder">
<link rel="icon" href="images/node-red.ico">
<!-- See https://goo.gl/OOhYW5 -->
<link rel="manifest" href="manifest.json">
<meta name="theme-color" content="#3f51b5">
<!-- Used if adding to homescreen for Chrome on Android. Fallback for manifest.json -->
<meta name="mobile-web-app-capable" content="yes">
<meta name="application-name" content="Node-RED UI Builder">
<!-- Used if adding to homescreen for Safari on iOS -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="Node-RED UI Builder">
<!-- Homescreen icons for Apple mobile use if required
<link rel="apple-touch-icon" href="/images/manifest/icon-48x48.png">
<link rel="apple-touch-icon" sizes="72x72" href="/images/manifest/icon-72x72.png">
<link rel="apple-touch-icon" sizes="96x96" href="/images/manifest/icon-96x96.png">
<link rel="apple-touch-icon" sizes="144x144" href="/images/manifest/icon-144x144.png">
<link rel="apple-touch-icon" sizes="192x192" href="/images/manifest/icon-192x192.png">
-->
<link rel="stylesheet" href="vendor/normalize.css/normalize.css" media="all">
<link rel="stylesheet" href="index.css" media="all">
</head>
<body>
<div id="app">
<h1>
UIbuilder + Moon for Node-RED
</h1>
<p>
This is a uibuilder test using <a href="http://moonjs.ga/">Moon.JS</a> as a front-end library.
See the
<a href="https://github.com/TotallyInformation/node-red-contrib-uibuilder">node-red-contrib-uibuilder</a>
README for details on how to use UIbuilder.
</p>
<h2>Dynamic Data (via Moon)</h2>
<p>
Messages: Received={{msgsReceived}}, Sent: {{msgsSent}}
</p><p>
Control Messages Received: {{msgsControl}}
</p>
<pre m-html="hLastRcvd"></pre>
<pre m-html="hLastSent"></pre>
<h2>Simple input using Moon</h2>
<p><button m-on:click="increment">Increment</button> Click Counter: {{counterBtn}}. Click on the button to increment, it sends the data back to Node-RED as well.</p>
</div>
<!-- These MUST be in the right order. Note no leading / -->
<!-- REQUIRED: Socket.IO is loaded only once for all instances
Without this, you don't get a websocket connection -->
<script src="/uibuilder/socket.io/socket.io.js"></script>
<!-- Note no leading / -->
<!-- OPTIONAL: JQuery can be removed if not required -->
<!-- <script src="vendor/jquery/dist/jquery.min.js"></script> -->
<!-- REQUIRED: Sets up Socket listeners and the msg object -->
<script src="vendor/moonjs/dist/moon.min.js"></script> <!-- //prod version -->
<!-- <script src="vendor/moonjs/dist/moon.js"></script> //dev version -->
<!-- <script src="uibuilderfe.js"></script> //dev version -->
<script src="uibuilderfe.min.js"></script> <!-- //prod version -->
<!-- OPTIONAL: You probably want this. Put your custom code here -->
<script src="index.js"></script>
</body>
</html>
/*global document,$,window,io */
/*
Copyright (c) 2017 Julian Knight (Totally Information)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* This is based on the default template js for uibuilder.
* It has been changed to remove JQuery but use MoonJS
*/
"use strict";
// Attach a Moon instance to html element with id "app"
const app1 = new Moon({
el: "#app",
data: {
startMsg : "Moon has started, waiting for messages",
feVersion : '',
counterBtn : 0,
msgsReceived: 0,
msgsControl : 0,
msgsSent : 0,
msgRecvd : '[Nothing]',
msgSent : '[Nothing]',
msgCtrl : '[Nothing]',
inputText : ''
}, // --- End of data --- //
computed: {
hLastRcvd: {
get: function() {
const msgRecvd = this.get('msgRecvd')
if (typeof msgRecvd === 'string') return 'Last Message Received = ' + msgRecvd
else return 'Last Message Received = ' + this.callMethod('syntaxHighlight', [msgRecvd])
}
},
hLastSent: {
get: function() {
const msgSent = this.get('msgSent')
if (typeof msgSent === 'string') return 'Last Message Sent = ' + msgSent
else return 'Last Message Sent = ' + this.callMethod('syntaxHighlight', [msgSent])
}
},
hMsgCtrl: {
get: function() {
const msgCtrl = this.get('msgCtrl')
if (typeof msgCtrl === 'string') return 'Last Message Sent = ' + msgCtrl
//else return 'Last Message Sent = ' + this.callMethod('syntaxHighlight', [msgCtrl])
else return 'Last Message Sent = ' + JSON.stringify(msgCtrl)
}
}
}, // --- End of computed --- //
methods: {
increment: function() {
// Increment the count by one
this.set('counterBtn', this.get('counterBtn') + 1)
let topic = (this.get('msgRecvd')).topic || 'uibuilder/moon'
uibuilder.send( { 'topic': topic, 'payload': { 'type': 'counterBtn', 'btnCount': this.get('counterBtn'), 'message': this.get('inputText') } } )
},
// return formatted HTML version of JSON object
syntaxHighlight: function(json) {
json = JSON.stringify(json, undefined, 4)
json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
var cls = 'number'
if (/^"/.test(match)) {
if (/:$/.test(match)) {
cls = 'key'
} else {
cls = 'string'
}
} else if (/true|false/.test(match)) {
cls = 'boolean'
} else if (/null/.test(match)) {
cls = 'null'
}
return '<span class="' + cls + '">' + match + '</span>'
})
} // --- End of syntaxHighlight --- //
}, // --- End of methods --- //
hooks: { // Available hooks: init,mounted,updated,destroyed
mounted: function(){
console.debug('app mounted - setting up uibuilder watchers')
this.set( 'feVersion', uibuilder.get('version'))
// If msg changes - msg is updated when a standard msg is received from Node-RED over Socket.IO
// Note that you can also listen for 'msgsReceived' as they are updated at the same time
// but newVal relates to the attribute being listened to.
uibuilder.onChange('msg', function(newVal){
console.info('property msg changed!', newVal)
app1.set( 'msgRecvd', newVal)
})
// As noted, we could get the msg here too
uibuilder.onChange('msgsReceived', function(newVal){
console.info('New msg sent FROM Node-RED over Socket.IO. Total Count: ', newVal)
app1.set( 'msgsReceived', newVal)
})
// If a message is sent back to Node-RED
uibuilder.onChange('sentMsg', function(newVal){
console.info('property sentMsg changed!', newVal)
app1.set( 'msgSent', newVal)
})
uibuilder.onChange('msgsSent', function(newVal){
console.info('New msg sent TO Node-RED over Socket.IO. Total Count: ', newVal)
app1.set('msgsSent', newVal )
})
// If we receive a control message from Node-RED
uibuilder.onChange('ctrlMsg', function(newVal){
console.info('property msgCtrl changed!', newVal)
app1.set( 'msgCtrl', newVal)
})
uibuilder.onChange('msgsCtrl', function(newVal){
console.info('New CONTROL msg sent FROM Node-RED over Socket.IO. Total Count: ', newVal)
app1.set('msgsControl', newVal )
})
// If Socket.IO connects/disconnects
uibuilder.onChange('ioConnected', function(newVal){
console.info('Socket.IO Connection Status Changed: ', newVal)
app1.set('socketConnectedState', newVal )
})
} // --- End of mounted hook --- //
} // --- End of hooks --- //
}) // --- End of app1 --- //
// EOF
body {font-family: sans-serif;}
div, p, code, pre { margin:0.3em; padding: 0.3em;}
pre .string { color: green; }
.number { color: darkorange; }
.boolean { color: blue; }
.null { color: magenta; }
.key { color: red; }
Please feel free to add comments to the page (clearly mark with your initials & please add a commit msg so we know what has changed). You can contact me in the Discourse forum, or raise an issue here in GitHub! I will make sure all comments & suggestions are represented here.
-
Walkthrough 🔗 Getting started
-
In Progress and To Do 🔗 What's coming up for uibuilder?
-
Awesome uibuilder Examples, tutorials, templates and references.
-
How To
- How to send data when a client connects or reloads the page
- Send messages to a specific client
- Cache & Replay Messages
- Cache without a helper node
- Use webpack to optimise front-end libraries and code
- How to contribute & coding standards
- How to use NGINX as a proxy for Node-RED
- How to manage packages manually
- How to upload a file from the browser to Node-RED
-
Vanilla HTML/JavaScript examples
-
VueJS general hints, tips and examples
- Load Vue (v2 or v3) components without a build step (modern browsers only)
- How to use webpack with VueJS (or other frameworks)
- Awesome VueJS - Tips, info & libraries for working with Vue
- Components that work
-
VueJS v3 hints, tips and examples
-
VueJS v2 hints, tips and examples
- Dynamically load .vue files without a build step (Vue v2)
- Really Simple Example (Quote of the Day)
- Example charts using Chartkick, Chart.js, Google
- Example Gauge using vue-svg-gauge
- Example charts using ApexCharts
- Example chart using Vue-ECharts
- Example: debug messages using uibuilder & Vue
- Example: knob/gauge widget for uibuilder & Vue
- Example: Embedded video player using VideoJS
- Simple Button Acknowledgement Example Thanks to ringmybell
- Using Vue-Router without a build step Thanks to AFelix
- Vue Canvas Knob Component Thanks to Klaus Zerbe
-
Examples for other frameworks (check version before trying)
- Basic jQuery example - Updated for uibuilder v6.1
- ReactJS with no build - updated for uibuilder v5/6
-
Examples for other frameworks (may not work, out-of-date)
-
Outdated Pages (Historic only)
- v1 Examples (these need updating to uibuilder v2/v3/v4/v5)