Uibuilder change images

Before you state this I did see the topic UIBuilder- Dynamically changing images, however for some reason it is just not clicking for me.

I am looking to change a large picture in my UIBuilder space (basically the background image at multiple times during the run of Node-red (running an escape room), and I want to change the image to reflect what is going on in the room. So I am thinking of doing this with z-index, to ensure that this image is always the "background" image. So I would do a img src tag..... how would I then change it in VueJS? I see would I set the image src to a variable in Node Red and then just update that variable to update the image? Or am I missing something else here?

(I am not at my location that has node red at the moment, and my time on location is limited, so I want to have the answers in front of me while on location, rather than stumbling through figuring it out while on location.)

In that case install node-red on your laptop or similar and experiment so you know exactly what to do when you get on site.

Thank you for the reply.... but that is already something I do have, it is difficult to recreate my node-red at another off-site location due to so many interconnecting parts with items that are on-location.

I was looking for code-examples to help me along. Thank you.

Here is the HTML code that is currently:

<img ref="backgroundOverlay" class="periodBackground"></p>

And in the CSS file:

.periodBackground {
    position: fixed;
    z-index: 5;
    top:0px;
    left:0px;
    background-image:none;
    padding: 0px;
    height:50%;
    width:100%;
    background-position:center;
    background-repeat:no-repeat;
    content: url("images/background.png");
}

Here is my JS file:

/* jshint browser: true, esversion: 5, asi: true */
/*globals Vue, uibuilder */
// @ts-nocheck
/*
  Copyright (c) 2021-2022 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.
*/
'use strict'

/** @see https://totallyinformation.github.io/node-red-contrib-uibuilder/#/front-end-library */

var lastSrc = "";
var lastAudioSrc = "";
var lastBackgroundSrc = "";

// eslint-disable-next-line no-unused-vars
const app = new Vue({
    el: '#app',

    data() { return {

        startMsg    : 'Vue has started, waiting for messages',
        feVersion   : '',
        counterBtn  : 0,
        inputText   : null,
        inputChkBox : false,
        socketConnectedState : false,
        serverTimeOffset     : '[unknown]',
        imgProps             : { width: 75, height: 75 },

        msgRecvd    : '[Nothing]',
        msgsReceived: 0,
        msgCtrl     : '[Nothing]',
        msgsControl : 0,

        msgSent     : '[Nothing]',
        msgsSent    : 0,
        msgCtrlSent : '[Nothing]',
        msgsCtrlSent: 0,

        isLoggedOn  : false,
        userId      : null,
        userPw      : null,
        inputId     : '',

    }}, // --- End of data --- //

    computed: {

        hLastRcvd: function() {
            var msgRecvd = this.msgRecvd
            if (typeof msgRecvd === 'string') return 'Last Message Received = ' + msgRecvd
            return 'Last Message Received = ' + this.syntaxHighlight(msgRecvd)
        },
        hLastSent: function() {
            var msgSent = this.msgSent
            if (typeof msgSent === 'string') return 'Last Message Sent = ' + msgSent
            return 'Last Message Sent = ' + this.syntaxHighlight(msgSent)
        },
        hLastCtrlRcvd: function() {
            var msgCtrl = this.msgCtrl
            if (typeof msgCtrl === 'string') return 'Last Control Message Received = ' + msgCtrl
            return 'Last Control Message Received = ' + this.syntaxHighlight(msgCtrl)
        },
        hLastCtrlSent: function() {
            var msgCtrlSent = this.msgCtrlSent
            if (typeof msgCtrlSent === 'string') return 'Last Control Message Sent = ' + msgCtrlSent
            return 'Last Control Message Sent = ' + this.syntaxHighlight(msgCtrlSent)
        },

    }, // --- End of computed --- //

    methods: {

        // Called from the increment button - sends a msg to Node-RED
        increment: function(event) {
            console.log('Button Pressed. Event Data: ', event)

            // Increment the count by one
            this.counterBtn += 1
            var topic = this.msgRecvd.topic || 'uibuilder/vue'
            uibuilder.send( {
                'topic': topic,
                'payload': {
                    'type': 'counterBtn',
                    'btnCount': this.counterBtn,
                    'message': this.inputText,
                    'inputChkBox': this.inputChkBox
                }
            } )

        }, // --- End of increment --- //

        // REALLY Simple method to return DOM events back to Node-RED. See the 2nd b-button on the default html
        doEvent: uibuilder.eventSend,

        // 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;')
            json = 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>'
            })
            return json
        }, // --- End of syntaxHighlight --- //

    }, // --- End of methods --- //

    // Available hooks: beforeCreate,created,beforeMount,mounted,beforeUpdate,updated,beforeDestroy,destroyed, activated,deactivated, errorCaptured

    /** Called after the Vue app has been created. A good place to put startup code */
    created: function() {

        // Example of retrieving data from uibuilder
        this.feVersion = uibuilder.get('version')

        /** **REQUIRED** Start uibuilder comms with Node-RED @since v2.0.0-dev3
         * Pass the namespace and ioPath variables if hosting page is not in the instance root folder
         * e.g. If you get continual `uibuilderfe:ioSetup: SOCKET CONNECT ERROR` error messages.
         * e.g. uibuilder.start('/uib', '/uibuilder/vendor/socket.io') // change to use your paths/names
         * @param {Object=|string=} namespace Optional. Object containing ref to vueApp, Object containing settings, or String IO Namespace override. changes self.ioNamespace from the default.
         * @param {string=} ioPath Optional. changes self.ioPath from the default
         * @param {Object=} vueApp Optional. Reference to the VueJS instance. Used for Vue extensions.
         */
        uibuilder.start(this) // Single param passing vue app to allow Vue extensions to be used.

        //console.log(this)

    }, // --- End of created hook --- //

    /** Called once all Vue component instances have been loaded and the virtual DOM built */
    mounted: function(){

        //console.debug('[indexjs:Vue.mounted] app mounted - setting up uibuilder watchers')

        var app = this  // Reference to `this` in case we need it for more complex functions

        // If msg changes - msg is updated when a standard msg is received from Node-RED over Socket.IO
        uibuilder.onChange('msg', function(msg){
            //console.info('[indexjs:uibuilder.onChange] msg received from Node-RED server:', msg)
            app.msgRecvd = msg
            app.msgsReceived = uibuilder.get('msgsReceived')
        })

        if (msg.videoOverlaySrc != lastSrc) {
            vueApp.$refs.videoOverlay.src = msg.videoOverlaySrc;
            lastSrc = msg.videoOverlaySrc;
        }
        if (msg.audioOverlaySrc != lastAudioSrc) {
            vueApp.$refs.audioOverlay.src = msg.audioOverlaySrc;
            vueApp.$refs.audioOverlay.play();
            lastAudioSrc = msg.audioOverlaySrc;
        }

        if (msg.backgroundImage != lastBackgroundSrc) {
            vueApp.$refs.backgroundOverlay.content = msg.backgroundImage;
            lastBackgroundSrc = msg.backgroundImage;
        }

        //#region ---- Debug info, can be removed for live use ---- //

        /** You can use the following to help trace how messages flow back and forth.
         * You can then amend this processing to suite your requirements.
         */

        // If we receive a control message from Node-RED, we can get the new data here - we pass it to a Vue variable
        uibuilder.onChange('ctrlMsg', function(msg){
            //console.info('[indexjs:uibuilder.onChange:ctrlMsg] CONTROL msg received from Node-RED server:', msg)
            app.msgCtrl = msg
            app.msgsControl = uibuilder.get('msgsCtrl')
        })

        /** You probably only need these to help you understand the order of processing
         * If a message is sent back to Node-RED, we can grab a copy here if we want to
         */
        uibuilder.onChange('sentMsg', function(msg){
            //console.info('[indexjs:uibuilder.onChange:sentMsg] msg sent to Node-RED server:', msg)
            app.msgSent = msg
            app.msgsSent = uibuilder.get('msgsSent')
        })

        /** If we send a control message to Node-RED, we can get a copy of it here */
        uibuilder.onChange('sentCtrlMsg', function(msg){
            //console.info('[indexjs:uibuilder.onChange:sentCtrlMsg] Control message sent to Node-RED server:', msg)
            app.msgCtrlSent = msg
            app.msgsCtrlSent = uibuilder.get('msgsSentCtrl')
        })

        /** If Socket.IO connects/disconnects, we get true/false here */
        uibuilder.onChange('ioConnected', function(connected){
            //console.info('[indexjs:uibuilder.onChange:ioConnected] Socket.IO Connection Status Changed to:', connected)
            app.socketConnectedState = connected
        })
        /** If Server Time Offset changes */
        uibuilder.onChange('serverTimeOffset', function(serverTimeOffset){
            //console.info('[indexjs:uibuilder.onChange:serverTimeOffset] Offset of time between the browser and the server has changed to:', serverTimeOffset)
            app.serverTimeOffset = serverTimeOffset
        })

        /** If user is logged on/off */
        uibuilder.onChange('isAuthorised', function(isAuthorised){
            //console.info('[indexjs:uibuilder.onChange:isAuthorised] isAuthorised changed. User logged on?:', isAuthorised)
            //console.log('authData: ', uibuilder.get('authData'))
            //console.log('authTokenExpiry: ', uibuilder.get('authTokenExpiry'))
            app.isLoggedOn = isAuthorised
        })

        //#endregion ---- Debug info, can be removed for live use ---- //

    }, // --- End of mounted hook --- //

}) // --- End of app1 --- //

// EOF

The best way to acheive this is almost certainly to create several CSS classes, each one with the correct background image defined.

Then simply make the class field on the controlling tag a Vue variable. Something like :class="myBackgroundClassVar".

Then the only complexity is which component to add that to. Normally, you have a parent <div id="app"> and everything within it can be controlled by Vue. But you really want to be able to control the <body> element. I believe this is possible but you will need to do a search for the right way.

Don't do background images using an img tag, it isn't very efficient. Do it using a class. Then you can also put it on any element that is displayed as a block. (e.g. a div, p, ... or indeed body).

I am not understanding what you mean. Sorry.

index.html:

<!doctype html>
<html lang="en"><head>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Epicenter Hotel/Casino Elevator Information System</title>

    <link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap/dist/css/bootstrap.min.css" />
    <link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.css" />
    <!-- Your own CSS -->
    <link type="text/css" rel="stylesheet" href="./index.css" media="all">

</head><body>
<div id="app" class="uib" v-cloak>
    <!-- All UI code needs to be in here -->
    <img :src='image1' ref="backgroundOverlay" class="periodBackground"></p>
    <p class="timerText">{{msg.formattedTimeRemaining}}</p>
    <p class="clueText">{{msg.clueText}}</p>
    <p class="exitCode">{{msg.exitCode}}</p>
    <p class="messageTest">R: <b>{{msgsReceived}}</b>, S: <b>{{msgsSent}}</b></p>

    <audio autoplay class="overlayAudio" ref="audioOverlay" src="/" type="audio/mp3">
    </audio>


    <b-container id="app_container">
        <!-- Wraps the bootstrap-vue formatting -->

    </b-container>
</div>

    <!-- #region Supporting Scripts. These MUST be in the right order. Note no leading / -->
    <script src="../uibuilder/vendor/socket.io/socket.io.js">/* REQUIRED: Socket.IO is loaded only once for all instances. Without this, you don't get a websocket connection */</script>
    <!-- Vendor Libraries - Load in the right order, use minified, production versions for speed -->
    <script src="../uibuilder/vendor/vue/dist/vue.min.js">/* prod version with component compiler */</script>
    <!-- <script src="../uibuilder/vendor/vue/dist/vue.js">/* dev version with component compiler */</script> -->
    <!-- <script src="../uibuilder/vendor/vue/dist/vue.runtime.min.js">/* prod version without component compiler */</script> -->
    <script src="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.min.js">/* remove 'min.' to use dev version */</script> <!--  -->

    <script src="./uibuilderfe.min.js">/* REQUIRED: remove 'min.' to use dev version */</script>
    <script src="./index.js">/* OPTIONAL: Put your custom code here */</script>
    <!-- #endregion -->

</body></html>

index.css:

/* Load the defaults from `<userDir>/node_modules/node-red-contrib-uibuilder/front-end/src/uib-styles.css` */
@import url("./uib-styles.css");

/* Cloak elements on initial load to hide the possible display of {{ ... }} 
 * Add to the app tag or to specific tags
 * To display "loading...", change to the following:
 *    [v-cloak] > * { display:none }
 *    [v-cloak]::before { content: "loading…" }
 */
[v-cloak] { display: none; }


body {
    padding: 0;
    margin: 0;
    //overflow: hidden; /* Don't display any scroll bars */
    background: url("images/background.png");
    background-size: cover;
    background-repeat: no-repeat;
}
#app {

    height: 1080px;
    width: 100%;
    text-align: center;
    margin: auto;
    font-family: 'Staatliches', cursive;
    color: #AAA;
}

.background, .backgroundVideo {
  position: fixed; 
  right: 0;
  bottom: 0;
  min-width: 100%;
  min-height: 100%;
  z-index: -10;
}

.overlay, .overlayVideo {
  position: fixed; 
  right: 0;
  bottom: 0;
  min-width: 100%;
  min-height: 100%;
  z-index: 10;
}

.overlayAudio {
    position: fixed;
    left: 0;
    top: 0;
    width: 200;
    height: 200;
    z-index: 25;
}

.timerText { 
    position: fixed;
    top:980px;
    left:1650px;
    font-size: 60px;
    z-index: 12;
    color: rgb(255, 255, 255);   
}
.clueText {
    position: fixed;
    text-align: center;
    top:650px;
    left:10px;
    transform: translate(-50%, -50%);
    font-size: 120px;
    z-index: 15;
    color: #C2F;
}
.exitCode {
    position: fixed;
    top:115px;
    left:185px;
    font-size: 225px;
    z-index: 14;
    color: rgb(255, 255, 255);
}
.messageTest {
    position: fixed;
    text-align: left;
    top:1040px;
    left:5px;
    font-size: 25px;
    z-index: 13;
    color:#FFF;
}
.periodBackground {
    position: fixed;
    z-index: 5;
    top:0px;
    left:0px;
    padding: 0px;
    height:50%;
    width:100%;
    content: url("images/background.png");
}

index.js

/* jshint browser: true, esversion: 5, asi: true */
/*globals Vue, uibuilder */
// @ts-nocheck
/*
  Copyright (c) 2021-2022 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.
*/
'use strict'

/** @see https://totallyinformation.github.io/node-red-contrib-uibuilder/#/front-end-library */

var lastSrc = "";
var lastAudioSrc = "";
var lastBackgroundSrc = "";

// eslint-disable-next-line no-unused-vars
const app = new Vue({
    el: '#app',

    data() { return {

        startMsg    : 'Vue has started, waiting for messages',
        feVersion   : '',
        counterBtn  : 0,
        inputText   : null,
        inputChkBox : false,
        socketConnectedState : false,
        serverTimeOffset     : '[unknown]',
        imgProps             : { width: 75, height: 75 },

        msgRecvd    : '[Nothing]',
        msgsReceived: 0,
        msgCtrl     : '[Nothing]',
        msgsControl : 0,

        msgSent     : '[Nothing]',
        msgsSent    : 0,
        msgCtrlSent : '[Nothing]',
        msgsCtrlSent: 0,

        isLoggedOn  : false,
        userId      : null,
        userPw      : null,
        inputId     : '',

    }}, // --- End of data --- //

    computed: {

        hLastRcvd: function() {
            var msgRecvd = this.msgRecvd
            if (typeof msgRecvd === 'string') return 'Last Message Received = ' + msgRecvd
            return 'Last Message Received = ' + this.syntaxHighlight(msgRecvd)
        },
        hLastSent: function() {
            var msgSent = this.msgSent
            if (typeof msgSent === 'string') return 'Last Message Sent = ' + msgSent
            return 'Last Message Sent = ' + this.syntaxHighlight(msgSent)
        },
        hLastCtrlRcvd: function() {
            var msgCtrl = this.msgCtrl
            if (typeof msgCtrl === 'string') return 'Last Control Message Received = ' + msgCtrl
            return 'Last Control Message Received = ' + this.syntaxHighlight(msgCtrl)
        },
        hLastCtrlSent: function() {
            var msgCtrlSent = this.msgCtrlSent
            if (typeof msgCtrlSent === 'string') return 'Last Control Message Sent = ' + msgCtrlSent
            return 'Last Control Message Sent = ' + this.syntaxHighlight(msgCtrlSent)
        },

    }, // --- End of computed --- //

    methods: {

        // Called from the increment button - sends a msg to Node-RED
        increment: function(event) {
            console.log('Button Pressed. Event Data: ', event)

            // Increment the count by one
            this.counterBtn += 1
            var topic = this.msgRecvd.topic || 'uibuilder/vue'
            uibuilder.send( {
                'topic': topic,
                'payload': {
                    'type': 'counterBtn',
                    'btnCount': this.counterBtn,
                    'message': this.inputText,
                    'inputChkBox': this.inputChkBox
                }
            } )

        }, // --- End of increment --- //

        // REALLY Simple method to return DOM events back to Node-RED. See the 2nd b-button on the default html
        doEvent: uibuilder.eventSend,

        // 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;')
            json = 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>'
            })
            return json
        }, // --- End of syntaxHighlight --- //

    }, // --- End of methods --- //

    // Available hooks: beforeCreate,created,beforeMount,mounted,beforeUpdate,updated,beforeDestroy,destroyed, activated,deactivated, errorCaptured

    /** Called after the Vue app has been created. A good place to put startup code */
    created: function() {

        // Example of retrieving data from uibuilder
        this.feVersion = uibuilder.get('version')

        /** **REQUIRED** Start uibuilder comms with Node-RED @since v2.0.0-dev3
         * Pass the namespace and ioPath variables if hosting page is not in the instance root folder
         * e.g. If you get continual `uibuilderfe:ioSetup: SOCKET CONNECT ERROR` error messages.
         * e.g. uibuilder.start('/uib', '/uibuilder/vendor/socket.io') // change to use your paths/names
         * @param {Object=|string=} namespace Optional. Object containing ref to vueApp, Object containing settings, or String IO Namespace override. changes self.ioNamespace from the default.
         * @param {string=} ioPath Optional. changes self.ioPath from the default
         * @param {Object=} vueApp Optional. Reference to the VueJS instance. Used for Vue extensions.
         */
        uibuilder.start(this) // Single param passing vue app to allow Vue extensions to be used.

        //console.log(this)

    }, // --- End of created hook --- //

    /** Called once all Vue component instances have been loaded and the virtual DOM built */
    mounted: function(){

        //console.debug('[indexjs:Vue.mounted] app mounted - setting up uibuilder watchers')

        var app = this  // Reference to `this` in case we need it for more complex functions

        // If msg changes - msg is updated when a standard msg is received from Node-RED over Socket.IO
        uibuilder.onChange('msg', function(msg){
            //console.info('[indexjs:uibuilder.onChange] msg received from Node-RED server:', msg)
            app.msgRecvd = msg
            app.msgsReceived = uibuilder.get('msgsReceived')
        })

        if (msg.videoOverlaySrc != lastSrc) {
            vueApp.$refs.videoOverlay.src = msg.videoOverlaySrc;
            lastSrc = msg.videoOverlaySrc;
        }
        if (msg.audioOverlaySrc != lastAudioSrc) {
            vueApp.$refs.audioOverlay.src = msg.audioOverlaySrc;
            vueApp.$refs.audioOverlay.play();
            lastAudioSrc = msg.audioOverlaySrc;
        }

        if (msg.backgroundImage != lastBackgroundSrc) {
            vueApp.$refs.periodBackground.content = "url( " + msg.backgroundImage + ")";
            lastBackgroundSrc = msg.backgroundImage;
        }

        //#region ---- Debug info, can be removed for live use ---- //

        /** You can use the following to help trace how messages flow back and forth.
         * You can then amend this processing to suite your requirements.
         */

        // If we receive a control message from Node-RED, we can get the new data here - we pass it to a Vue variable
        uibuilder.onChange('ctrlMsg', function(msg){
            //console.info('[indexjs:uibuilder.onChange:ctrlMsg] CONTROL msg received from Node-RED server:', msg)
            app.msgCtrl = msg
            app.msgsControl = uibuilder.get('msgsCtrl')
        })

        /** You probably only need these to help you understand the order of processing
         * If a message is sent back to Node-RED, we can grab a copy here if we want to
         */
        uibuilder.onChange('sentMsg', function(msg){
            //console.info('[indexjs:uibuilder.onChange:sentMsg] msg sent to Node-RED server:', msg)
            app.msgSent = msg
            app.msgsSent = uibuilder.get('msgsSent')
        })

        /** If we send a control message to Node-RED, we can get a copy of it here */
        uibuilder.onChange('sentCtrlMsg', function(msg){
            //console.info('[indexjs:uibuilder.onChange:sentCtrlMsg] Control message sent to Node-RED server:', msg)
            app.msgCtrlSent = msg
            app.msgsCtrlSent = uibuilder.get('msgsSentCtrl')
        })

        /** If Socket.IO connects/disconnects, we get true/false here */
        uibuilder.onChange('ioConnected', function(connected){
            //console.info('[indexjs:uibuilder.onChange:ioConnected] Socket.IO Connection Status Changed to:', connected)
            app.socketConnectedState = connected
        })
        /** If Server Time Offset changes */
        uibuilder.onChange('serverTimeOffset', function(serverTimeOffset){
            //console.info('[indexjs:uibuilder.onChange:serverTimeOffset] Offset of time between the browser and the server has changed to:', serverTimeOffset)
            app.serverTimeOffset = serverTimeOffset
        })

        /** If user is logged on/off */
        uibuilder.onChange('isAuthorised', function(isAuthorised){
            //console.info('[indexjs:uibuilder.onChange:isAuthorised] isAuthorised changed. User logged on?:', isAuthorised)
            //console.log('authData: ', uibuilder.get('authData'))
            //console.log('authTokenExpiry: ', uibuilder.get('authTokenExpiry'))
            app.isLoggedOn = isAuthorised
        })

        //#endregion ---- Debug info, can be removed for live use ---- //

    }, // --- End of mounted hook --- //

}) // --- End of app1 --- //

// EOF

function node:

var p = "background-old.png";
msg.backgroundImage = "images/" + p;
return msg;

You don't need to do that. And you have a rogue </p>.

Instead add a CSS background statement for the div with the id app.

.background1 {
    background-image: url("./path/to/default-image.png");
}

.background2 {
    background-image: url("./path/to/image-2.png");
}

Then change the HTML for the div to:

<div id="app" :class="[bgImgClass, 'uib']" v-cloak>

(Untested, I think I got that right for a list of classes but I might have got it wrong - check with the Vue docs)

And in your js file, you need to add the bgImgClass variable to the Vue data variables and set its default to the default image class.

Then you can send a msg from Node-RED when you want to change the image, simply send the next class name and use a change function to update the Vue variable - something like:

uibuilder.onChange('msg', (msg) => {
    if ( msg.background ) { // This will contain the class name
        app.bgImgClass = msg.background
    }
})

Just note that this approach might leave you with an edge around your background because you've applied it to a div rather than body. Tweaking the #uib class should let you eliminate that if it happens.

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.