-
-
Notifications
You must be signed in to change notification settings - Fork 86
Example: RiotJS
Here is a quick example using RIOT.
In ~/.node-red/package.json
(along with whatever you normally have):
"dependencies": {
"node-red-contrib-uibuilder": "*",
"riot": "^3.6.1"
}
In the module.exports part of your settings.js
file, add this:
uibuilder: {
userVendorPackages: ['riot'],
debug: true
}
Flow:
[{"id":"502557a7.e19678","type":"debug","z":"106ba95c.ff91e7","name":"","active":true,"console":"false","complete":"true","x":570,"y":100,"wires":[]},{"id":"c6005228.32f9c","type":"uibuilder","z":"106ba95c.ff91e7","name":"a","url":"riot/","fwdInMessages":true,"customFoldersReqd":true,"x":419.9461975097656,"y":100.18403625488281,"wires":[["502557a7.e19678"]]},{"id":"db7c70c3.20f8f","type":"change","z":"106ba95c.ff91e7","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"{ \"ts\": $now(), \"cool\":\"very\"}","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":260,"y":100,"wires":[["c6005228.32f9c"]]},{"id":"865a81d8.4e333","type":"inject","z":"106ba95c.ff91e7","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":100,"y":100,"wires":[["db7c70c3.20f8f"]]}]
The flow should create a ~/.node-red/uibuilder/riot folder
. In the src
subfolder, add index.css
, index.html
, index.js
, with-tags.tag
index.css:
h1 { color: #448}
h2 { color: #585}
index.html:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">
<title>Node-RED UI Builder - RiotJS</title>
<meta name="description" content="Node-RED UI Builder - Testing RiotJS">
<link rel="icon" href="images/logo-red.png">
<!-- See https://goo.gl/OOhYW5 -->
<link rel="manifest" href="manifest.json">
<meta name="theme-color" content="#3f51b5">
<!-- Add 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">
<!-- Add 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
<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">
<link rel="stylesheet" href="index.css">
<!-- <script src="js/vendor/modernizr-2.8.3.min.js"></script> -->
</head>
<body>
<h1>Testing RiotJS</h1>
<!-- inlined tag definition, Custom tags can be empty, HTML only or JavaScript only -->
<my-tag></my-tag><!-- mount point -->
<script type="riot/tag">
<my-tag>
<h2>(0) Some local stuff first</h2>
<p>
msgCounter (global JS variable): {msgCounter.data}
</p>
<p>This only updates when riot.update is called. We call that in <i>index.js</i> when we receive a new msg from SocketIO.</p>
<h2>(1) Next is an HTML-only tag, no JS allowed ...</h2>
<tag1>
<p> We get no local html here so this doesn't show up unless you use <yield/> in your tag definition.</p>
</tag1>
<h2>(2) Next up, a JavaScript only Riot tag (see the console log) ...</h2>
<tag2>
<p>
HTML and content is inside the tag, the tag definition is only Javascript.
The next line is an attribute (tag2Msg) from the JavaScript:
</p>
{ tag2Msg }
</tag2>
<tag3 />
<h2>(4) Next, an external tag file (<i>with-tags.js</i>) ...</h2>
<example message="We are passing a message!"/>
this.on('update', function() {
// allows recalculation of context data before the update
console.log('my-tag was updated')
console.log(msgCounter.data)
//console.log(msg)
})
</my-tag>
<tag1>
<p>This is an HTML-only tag, no JS allowed. So the following will be empty:</p>
<p>A message on mount: { opts.mymsg }</p>
<p>But using <yield/> lets us show information inserted where the tag is mounted (see <i>my-tag</i> in the source):</p>
<div style="margin-left:20%;margin-right:20%;border:1px solid silver;text-align:center;">
<yield/>
</div>
</tag1>
<tag2>
// This is a JavaScript-only tag, no HTML allowed
this.tag2Msg = 'Welcome to tag2'
console.log('tag2, a JavaScript-only tag')
this.on('mount', function(){
console.log('tag2 was mounted!')
})
</tag2>
<tag3>
<h2 onclick={updateMe}>(3) Another tag ...</h2>
<p></p>
<p>Parent opts.mymsg: { parent.opts.mymsg }</p>
<p>Global msgCounter.data: { msgCounter.data }</p>
<p>This mymsg: { mymsg }</p>
var self = this
this.mymsg = 'Poo'
this.on('mount', function(){
//self.update()
console.log('tag3 was mounted')
})
this.on('update', function() {
// allows recalculation of context data before the update
console.log('tag3 was updated')
})
updateMe(e) {
console.log('tag3: updateMe')
self.mymsg = 'You clicked milord?'
console.dir(e)
//console.log(parent.opts.msgCounter)
//self.update()
}
</tag3>
</script>
<!-- <example/> is specified on external file -->
<script data-src="with-tags.tag" type="riot/tag"></script>
<h2>(5) That's it for RiotJS content. See the Javascript file as well.</h2>
<p>
There really isn't much point using anything other than JQuery unless
you need more complex interactions with the page. Simple pages are best done
just with JQuery.
</p>
<h1>Dynamic Data (via JQuery)</h1>
<p>Messages Received: <span id="msgsReceived"></span></p>
<p>Control Messages Received: <span id="msgsControl"></span></p>
<p>Messages Sent: <span id="msgsSent"></span></p>
<p>Last Message Received:</p>
<code id="showMsg"></code>
<p>Last Message Sent:</p>
<code id="showMsgSent"></code>
<!-- Socket.IO is loaded only once for all instances -->
<script src="/uibuilder/socket.io/socket.io.js"></script>
<!-- Note no leading / -->
<script src="vendor/jquery/dist/jquery.min.js"></script>
<!-- include riot.js and the compiler -->
<script type="text/javascript" src="vendor/riot/riot+compiler.min.js"></script>
<script src="uibuilderfe.min.js"></script>
<script src="index.js"></script>
<!-- mount normally -->
<script>
</script>
</body>
</html>
index.js
/*global document,$,window,io,riot */
/*
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.
*/
var msgCounter = {control: 0, sent: 0, data: 0},
msg = {},
timerid
uibuilder.debug(true)
// When JQuery is ready, update
$( document ).ready(function() {
$('#msgsReceived').text(msgCounter.data)
$('#msgsControl').text(msgCounter.control)
$('#msgsSent').text(msgCounter.sent)
$('#showMsg').text(JSON.stringify(msg))
riot.mount('*', {'msg': msg, 'mymsg': 'Hello there'})
// When Node-RED uibuilder template node sends a msg over Socket.IO...
uibuilder.onChange('msg', function(recievedMsg) {
// Make sure that msg is an object & not null
if ( recievedMsg === null ) {
recievedMsg = {}
} else if ( typeof recievedMsg !== 'object' ) {
recievedMsg = { 'payload': recievedMsg }
}
msg = recievedMsg
// Track how many messages have been recieved
msgCounter.data++
$('#msgsReceived').text(msgCounter.data)
$('#showMsg').text(JSON.stringify(msg))
// Note that, unlike more complex libraries, Riot.JS does
// NOT update data dynamically! You have to call update.
riot.update()
// TODO: Add a check for a pre-defined global function here
// to make it easier for users to add their own code
// to process reciept of new msg
// OR MAYBE use msg.prototype to add a function?
// Test auto-response
if (uibuilder.get('debug')) {
sendMsg({payload: 'We got a message from you, thanks'})
}
}) // -- End of websocket recieve DATA msg from Node-RED -- //
// Recieve a CONTROL msg from Node-RED
uibuilder.onChange('ctrlMsg', function(recievedCtrlMsg) {
// Make sure that msg is an object & not null
if ( recievedCtrlMsg === null ) {
recievedCtrlMsg = {}
} else if ( typeof recievedCtrlMsg !== 'object' ) {
recievedCtrlMsg = { 'payload': recievedCtrlMsg }
}
msgCounter.control++
$('#msgsControl').text(msgCounter.control)
$('#showMsg').text(JSON.stringify(recievedCtrlMsg))
switch(recievedCtrlMsg.type) {
case 'shutdown':
// We are shutting down
break
case 'connected':
// We are connected to the server
break
default:
// Anything else
}
// Test auto-response
if (uibuilder.get('debug')) {
sendMsg({payload: 'We got a control message from you, thanks'})
}
}) // -- End of websocket recieve CONTROL msg from Node-RED -- //
});
// ----- UTILITY FUNCTIONS ----- //
// send a msg back to Node-RED, NR will generally expect the msg to contain a payload topic
var sendMsg = function(msgToSend) {
// Track how many messages have been sent
msgCounter.sent++
$('#msgsSent').text(msgCounter.sent)
$('#showMsgSent').text(JSON.stringify(msgToSend))
uibuilder.send(msgToSend)
} // --- End of Send Msg Fn --- //
// ----------------------------- //
// EOF
And finally, with-tags.tag:
<example>
<h1 show={this.show_message} onclick={uppercase}>{ message }</h1>
<input onkeyup={show_text} ref="input_text"/>
<button onclick={toggle_message}> toggle message </button>
<p>Parent mymsg: { this.parent.opts.mymsg }</p>
<p>Parent Keys:</p>
<ul>
<li each={ item, i in aparent }>
({ i }) { item }
</li>
</ul>
<p>Parent Opts Keys:</p>
<ul>
<li each={ item, i in Object.keys(this.parent.opts) }>
({ i }) { item }
</li>
</ul>
<p>Parent Opts Msg Keys:</p>
<ul>
<li each={ item, i in Object.keys(this.parent.opts.msg) }>
({ i }) { item }
</li>
</ul>
<p>Parent Msg Type: { typeof this.parent.opts.msg }</p>
<div if={Object.keys(msg).length}>
<p>Global Msg:</p>
<ul if={Object.keys(msg.payload).length}>
<li each={ item, i in msg.payload }>
{ i }: { item }
</li>
</ul>
</div>
<script>
this.message = opts.message || 'Hello Riot'
this.show_message = true
this.mymsg = opts.mymsg || this.parent.opts.mymsg
this.pmsg = this.parent.opts.msg
this.aparent = Object.keys(this.parent)
uppercase() {
this.message = this.message.toUpperCase()
}
this.show_text = function(e) {
this.message = this.refs.input_text.value
}
this.toggle_message = function(e) {
this.show_message = !this.show_message
}
console.info('Riot: Loaded this from external file ./with-tags.tag')
</script>
</example>
Now you should have the same demo that I have installed.
The resulting page will be on the /riot path. Click the inject from NR admin and you will see data coming into the riot page.
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)