Skip to content

Example, MoonJS with Mini.CSS

Julian Knight edited this page Oct 28, 2017 · 6 revisions

This is a slightly more advanced example than the basic MoonJS one. The installation and setup is the same, use the following files.

Note that processing of updated from Node-RED happens in the mounted hook. That is one way of setting up the change processes. Feel free to think up other ways and add them to the WIKI!

index.html

<!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-blue.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="https://gitcdn.link/repo/Chalarangelo/mini.css/master/dist/mini-default.min.css">
    <link rel="stylesheet" href="index.css" media="all">

</head>
<body>
    <div class="container" id="app">
        <header class="sticky row">
            <div class="col-sm col-md-10 col-md-offset-1">
                <span class="logo">UIbuilder + Moon + Mini.css for Node-RED</span>
            </div>
        </header>
        <div class="row">
            <p class="col-sm-12 col-md-10 col-md-offset-1">
                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>
            <div class="col-sm-12 col-md-10 col-md-offset-1 bordered rounded">

                <h2>Dynamic Data (via Moon)</h2>

                <p>UIBuilder Front-End Version: {{feVersion}}</p>
                <p>Websocket State: {{socketConnectedState}}</p>
                <p>
                    Messages: Received={{msgsReceived}}, Sent: {{msgsSent}}
                </p>
                <p>
                    Control Messages Received: {{msgsControl}}, Msg: {{hMsgCtrl}}
                </p>
                <h2>Simple input using Moon</h2>
                <p><input class="input" type="text" m-model="inputText"></p>
                <p><button class="primary shadowed" m-on:click="increment">Increment &amp; Send</button> Click Counter: {{counterBtn}}. Click on the button to increment, it sends the data back to Node-RED as well.</p>
            </div>
        </div>
        <div class="row">
                <h2 class="col-sm-12 col-md-10 col-md-offset-1" >Input and Output Messages</h2>
                <pre class="col-sm-12 col-md-5 col-md-offset-1" m-html="hLastRcvd"></pre>
                <pre class="col-sm-12 col-md-5" m-html="hLastSent"></pre>
        </div>
        <footer class="row">
            <div class="col-sm-12 col-md-10 col-md-offset-1">
                Copyright &copy; Julian Knight 2017 | Content generated by Node-RED and MoonJS
            </div>
        </footer>
    </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>

index.js

/*global document,window,Moon,uibuilder */
// @ts-ignore
/*
  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 Front-End JavaScript for uibuilder
 * It has been changed to remove JQuery and use MoonJS instead.
 * 
 * uibuilderfe.js defines the following globals: 
 *   uibuilder: The main global object containing the following...
 *     Methods:
 *       .onChange(attribute, callbackFn) - listen for changes to attribute and execute callback when it changes
 *       .get(attribute)        - Get any available attribute
 *       .set(attribute, value) - Set any available attribute (can't overwrite internal attributes)
 *       .msg                   - Shortcut to get the latest value of msg. Equivalent to uibuilder.get('msg')
 *       .send(msg)             - Shortcut to send a msg back to Node-RED manually
 *       .debug(true/false)     - Turn on/off debugging
 *       .uiDebug(type,msg)     - Utility function: Send debug msg to console (type=[log,info,warn,error,dir])
 *     Attributes with change events (only accessible via .get method except for msg)
 *       .msg          - Copy of the last msg sent from Node-RED over Socket.IO
 *       .send         - Copy of the last msg sent by us to Node-RED
 *       .ctrlMsg      - Copy of the last control msg received by us from Node-RED (Types: ['shutdown','server connected'])
 *       .msgsReceived - How many standard messages have we received
 *       .msgsSent     - How many messages have we sent
 *       .msgsCtrl     - How many control messages have we received
 *       .ioConnected  - Is Socket.IO connected right now? (true/false)
 *     Attributes without change events
 *           (only accessible via .get method, reload page to get changes, don't change unless you know what you are doing)
 *       .debug       - true/false, controls debug console logging output
 *       ---- You are not likely to need any of these ----
 *       .version     - check the current version of the uibuilder code
 *       .ioChannels  - List of the channel names in use [uiBuilderControl, uiBuilderClient, uiBuilder]
 *       .retryMs     - starting retry ms period for manual socket reconnections workaround
 *       .retryFactor - starting delay factor for subsequent reconnect attempts
 *       .ioNamespace - Get the namespace from the current URL
 *       .ioPath      - make sure client uses Socket.IO version from the uibuilder module (using path)
 *       .ioTransport - ['polling', 'websocket']
 *
 *   makeMeAnObject(thing, attribute='payload') - Utility function: make sure that 'thing' is an object
 */
"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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
            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

index.css

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; }
``
Clone this wiki locally