Problem with msgRecvd in uibuilder

I want to use the uibuilder node to make a page that get data from user. This data contains a name .
The ui should take that name and send it to the flow to check if it is not repeated in the database and returns to the ui with true or false.
I recieve the true or false message and save it in msgRecvd and use it later in increment function in methods.
The problem is that it is not readable inside increment function.
I've done this scenario before and it works . but in this project I've been trying for 2 days and I can't see what is wrong
If someone could help, I'd be grateful

This is my javascript for the page:

/*globals Vue, uibuilder */
// @ts-nocheck
/*
  Copyright (c) 2019 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://github.com/TotallyInformation/node-red-contrib-uibuilder/wiki/Front-End-Library---available-properties-and-methods */

// eslint-disable-next-line no-unused-vars
var app1 = new Vue({
    el: '#app',
    data: {
        startMsg    : 'Vue has started, waiting for messages',
        msgRecvd: [],
        
    }, // --- End of data --- //
    computed: {
        
    }, // --- End of computed --- //
    methods: {
        increment: function() {
            var name= document.getElementById("name").value;
            var msgRecvd, datacorrect , namec ;
            
            uibuilder.send( {
                'topic': "checkDB",
                'payload': {
                    'name': name
                }  });
            namec = this.msgRecvd
            console.log(this.msgRecvd)
            uibuilder.send( {
                        'topic':'test',
                        'payload': this.msgRecvd,
            })
        
    }, // --- End of methods --- //

    // Available hooks: init,mounted,updated,destroyed
    mounted: function(){
        
        uibuilder.start()
        var vueApp = this
        vueApp.feVersion = uibuilder.get('version')

        
        
        uibuilder.onChange('msg', function(newVal){
            vueApp.msgRecvd = newVal.payload
            
        })
    }

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

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

// EOF
type or paste code here

Hi Verina,

There are a few issues with your code.

  1. You havent shared the html file so there no way to check how you trigger the increment method
  2. In your Vue data you define msgRecvd as an array yet in your increment method you redefine it ?! does it need to be an array?
  3. No need to use vanilla javascript like getElementById since Vue has commands like v-model to synch a value in Vue data. Two way data binding

2- I removed the redefine of msgRecvd and I defined it as msgRecvd: ' ' in the data because it's not supposed to be an array anyway, it's just boolean variable
But there is no difference. in the increment method, it read ' ' , meanwhile in the .onchange it is saved and i can read its value by console.log
3- p.s. I'm used to the javascript without vue so I write it automatically, but I'll change to v-model later
1- this is the html code: (it has more input fields but I'm focusing on the name for now)

<!doctype html>
<html lang="en">
<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 - VueJS + bootstrap-vue version">

    <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">

    <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" />
    
    <!--<link type="text/css" rel="stylesheet" href="./index.css" media="all">-->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
    <link type="text/css" rel="stylesheet" href="./style.css" media="all">
    
</head>
<body class="no-scrolling">   
    <div id="app" class="wrapper" >
        <header>
			<div class=" img-container">
				<img src="/logo.png" alt="Insert Logo Here">
			</div>
			<nav aria-label="breadcrumb">
                <ol class="breadcrumb">
                    <li class="breadcrumb-item"><a href="#">Home</a></li>
                    <li class="breadcrumb-item"><a href="http://localhost:1880/Commands/Commands.html">Commands</a></li>
                    <li class="breadcrumb-item active" aria-current="page" style="font-size:20; color:white; opacity:0.7;">Add</li>
                </ol>
            </nav>
        </header>
            
		<b-container id="app_container" class="myform" >
		    <h1>Add a new Command to the System</h1>
            <div class="form">       
                <form name="form1" action="#">
                    <div class="form-group row">
                        <label for="name" class="col-sm-3 col-form-label">Command Name <code>*</code></label>
                        <div class="col-sm-9">
                            <input type="text" class="form-control" id="name" name="name" placeholder="Command Name.." required> <!--v-model="inputName" -->
                            <span id='errorname' style="color:red; font-weight:bold;"></span>
                        </div>
                    </div>
                    
                    <div class="form-group row">
                        <label for="type" class="col-sm-3 col-form-label">Command Type <code>*</code></label>
                        <div class="col-sm-3">
                            <select class="form-control" id="type" name="type" required>
                                <option value="">Choose Command Type...</option>
                                <option value="String">String</option>
                                <option value="Hexadecimal">Hexadecimal</option>
                                <option value="Decimal">Decimal</option>
                            </select>
                        </div>
                    </div>
  
                    <div class="form-group row">
                        <label for="command" class="col-sm-3 col-form-label">Command <code>*</code></label>
                        <div class="col-sm-9">
                            <input type="text" class="form-control" id="command" name="command" placeholder="Command Value.." required>
                            <span id='errordata' style="color:red; font-weight:bold;"></span>
                            <!--<div style="display:block inline;"><p id="invalidip" style="display:none;">Invalid IP Address</p></div>-->
                        </div>
                    </div>
                    
                    <div class="form-group row">
                        <label for="action" class="col-sm-3 col-form-label">Action Type <code>*</code></label>
                        <div class="col-sm-3">
                            <select class="form-control" name="action" id="action" required>
                                <option value="">Choose Action Type...</option>
                                <option value="Control">Control</option>
                                <option value="Notcontrol">not control</option>
                            </select>
                        </div>
                    </div>
                    
                    <div class="form-group row">
                        <div class="col-sm-10">
                            <button  class="btn" id="btn_increment" name="submit" v-on:click="bla" >Add</button>
                        </div>
                    </div>
                </form>
                
                <!-- Modal -->
            <div class="modal fade" id="successmodal" data-backdrop="static" data-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
                <div class="modal-dialog">
                    <div class="modal-content">
                        <div class="modal-header">
                            <h5 class="modal-title" id="staticBackdropLabel">Add a new Command</h5>
                            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                                <span aria-hidden="true">&times;</span>
                            </button>
                        </div>
                        <div class="modal-body">
                            The Command is added to the System successfully.
                            <br>
                            You can assign it to any Gateway Now!
                        </div>
                        
                    </div>
                </div>
            </div>
            
            </div> 
        </b-container>
        <footer>
			<h6>Alfa Group © 2020 All rights reserved.</h6>
    	</footer>
    </div>
    <script src="../uibuilder/vendor/socket.io/socket.io.js"></script>
    <script src="../uibuilder/vendor/vue/dist/vue.js"></script> 
    <script src="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.js"></script>
    <script src="./uibuilderfe.min.js"></script> 
    <script src="./setcommand.js"></script>

</body>

</html>

In your html the increment method is never called .. instead on your button you have v-on:click="bla"
im guessing its with this 'Add' btn that you want to send the form values and it should have been :

<b-button class="btn" id="btn_increment" name="submit" v-on:click.prevent="increment">Add</b-button>

(** the .prevent command is used to prevent the actual submission of the form and the refresh of the page/app.)

<input type="text" class="form-control" id="name" name="name" placeholder="Command Name.." required> <!--v-model="inputName" -->

Regarding storing the 'Name' value in Vue's data. You have the v-model syntax correct as a Comment :wink:

<input v-model="inputName" type="text" class="form-control" id="name" name="name" placeholder="Command Name.." required>

Use it .. and also define in Vue data the inputName

data: {
        startMsg    : 'Vue has started, waiting for messages',
        msgRecvd: '',
        inputName:''

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

As far the increment method goes it should be as simple as :

methods: {
        increment: function() {
                     
            uibuilder.send( {
                'topic': "checkDB",
                'payload': {
                    'name': this.inputName
                }  });
          
            }
    }, // --- End of methods --- //

The above should send the inputName from your form with uibuilder.send to Node-Red.

how you handle the reply from your db back to uibuilder needs some more thought and code processing in uibuilder.onChange but i hope the above suggestions help a bit.

That's a useful tip. In the template code that this seems to be based off, the function has a line that does the cancel of the default action.

Hi, you seem to have got yourself turned around quite a bit in the code. My suggestion would be not to use the variable and method names from the original template. Rather, keep those as-is and add your own code. That way, you can easily compare what you have written against what I wrote.

Create the Vue HTML first. Then add variables to the HMTL and Vue data section. Then add the method that will do the uibuilder.send().

As you are developing, consider removing the .min from the various Vue/bootstrap-vue/uibuilderfe libraries so that you get the development versions. Also add the VueJS debugging extension to your browser so that you can see the interactions more clearly. Remember to switch back to the minimised versions of the libraries when you have finished developing as that will be a lot quicker.

Thanks for the tips. I'm going to use them.

Also,This part solved most of my problem and I don't know what it means

/** @see https://github.com/TotallyInformation/node-red-contrib-uibuilder/wiki/Front-End-Library---available-properties-and-methods */

// eslint-disable-next-line no-unused-vars

I still have one problem that the variable msgRecvd is used before it changes but I'll try to figure that.

You mean the eslint part? That doesn't make any difference to your code, only to the linter.

That is true of all Vue data variables of course. The app is (has to be) built before you receive a msg because otherwise the variable could not be shown and would not be responsive. There are various tricks you can do to hide any visual issues you may have before you get your first data.

The most important is to give your variables sensible initial values.

You can also try adding v-cloak to the outer div: <div id="app" v-cloak> doesn't always help but sometimes it does.

I'm sorry, I don't fully understand what you said.
but I'm in a situation where I need to check a user input if exists in the database and return with msg true or false.
So I don't have a problem that the app is already built because I need a user input first before recieving a msg.
My problem is that it runs the code (that is supposed to be after recieving the msg) before updating the msgRecvd
This is my js now:

/* jshint browser: true, esversion: 5, asi: true */
/*globals Vue, uibuilder */
// @ts-nocheck
/*
  Copyright (c) 2019 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://github.com/TotallyInformation/node-red-contrib-uibuilder/wiki/Front-End-Library---available-properties-and-methods */

// eslint-disable-next-line no-unused-vars
var app1 = new Vue({
    el: '#app',
    data: {
        startMsg    : 'Vue has started, waiting for messages',
        feVersion   : '',
        name   : '',
        type : '',
        command : '',
        action : '',
        
        msgRecvd  : false,
        
        msgSent     : '[Nothing]',
        
        
    }, // --- End of data --- //
    computed: {
        
    }, // --- End of computed --- //
    methods: {
        validatename: function(name) {
            var vueApp = this;
            var namecorrect;
            uibuilder.send( {
                                'topic': "checkDB",
                                'payload': {
                                'name': name
                }  });
                /*for (var i = 0; i < 10000; i++) {
                    console.log(i);
                }*/
                
           namecorrect = this.msgRecvd;
            console.log(this.msgRecvd);
            uibuilder.send( {
                'topic':'test',
                 'payload': vueApp.msgRecvd,
             })
            return namecorrect;
        },
        
        validatedata: function(command,type) {
            var datacorrect;
                var hexformat = /^[-+]?[0-9A-Fa-f]+\.?[0-9A-Fa-f]*?$/;
                var decformat = /^[1-9]\d*$/;
                if(type === "Hexadecimal" && command.match(hexformat))  datacorrect = 1;
                else if(type === "Decimal" && command.match(decformat)) datacorrect = 1;
                else if(type === "String") datacorrect = 1;
                else if(type === "Hexadecimal" && !command.match(hexformat)) datacorrect = 0;
                else if(type === "Decimal" && command.match(decformat)) datacorrect = 0;
                return datacorrect;
        },
        
        increment: function(event) {
            var vueApp = this;
            var  datacorrect , namecorrect ;
            var name= this.name;
            namecorrect = vueApp.validatename(name);
            if(namecorrect)  {
            // if(name && type && command && action) {
                var type= this.type;
                var command= this.command;
                var action= this.action;
                var topic = this.msgRecvd.topic || 'uibuilder/vue';
                
            
                datacorrect = vueApp.validatedata(command,type);
                
                
                    
                if(namecorrect && datacorrect ) {
                    console.log("entered")
                    uibuilder.send( {
                        'topic': 'insertDB',
                        'payload': {
                        'name': name,
                        'type': type,
                        'command': command,
                        'action': action,
                    }  });
                    $('#successmodal').modal({ show: false});
                    $('#successmodal').modal('toggle');
                    $('#name').val(""); $('#type').val(""); $('#command').val(""); $('#action').val("");
                    document.getElementById("errorname").innerHTML="";
                    document.getElementById("errordata").innerHTML="";
                        
                }
                else {
                    if (!namecorrect) {
                        document.getElementById("errorname").innerHTML  = 'Invalid Name!!' + ' The name already exists in the database.' + vueApp.msgRecvd 
                        $('#name').val(""); 
                    }
                    if (!datacorrect) {
                        document.getElementById("errordata").innerHTML  = 'Invalid format!!' + ' The data is not in the ' + type + ' format.' + 
                        " Make sure You chose the right Command Type.." ;
                        $('#command').val("");   
                    }
                    
                }
            //}
             }

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

    // Available hooks: init,mounted,updated,destroyed
    mounted: function(){
        
        uibuilder.start()

        var vueApp = this

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

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

        //#region ---- Trace Received Messages ---- //
        // If msg changes - msg is updated when a standard msg is received from Node-RED over Socket.IO
        // newVal relates to the attribute being listened to.
        
        uibuilder.onChange('msg', function(newVal){
            console.log('update function')
            //console.info('[indexjs:uibuilder.onChange] msg received from Node-RED server:', newVal)
            vueApp.msgRecvd = newVal.payload
        })

        

        //#region ---- Trace Sent Messages ---- //
        // 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(newVal){
            //console.info('[indexjs:uibuilder.onChange:sentMsg] msg sent to Node-RED server:', newVal)
            vueApp.msgSent = newVal
        })
        // Updated count of sent messages
        uibuilder.onChange('msgsSent', function(newVal){
            //console.info('[indexjs:uibuilder.onChange:msgsSent] Updated count of msgs sent:', newVal)
            vueApp.msgsSent = newVal
        })

        

        

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

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

// EOF

Is there a way this situation can be handled?

Indeed the sequence of events seems wrong.
Why dont you simply call your validation methods when a msg is received?
In uibuilder.onChange('msg', function(newVal)

Steps

  1. Submit the form data .. using increment method. (sends a msg to node-red)
  2. When a reply comes back in uibuilder.onChange do a call for each of your validation methods.
    (ofcourse a few additional if statements may be needed to catch any errors like if name field is empty etc)

ps. a small recommendation to use a more meaningful name for your increment function .. like submitForm ? :wink:

1 Like

Thans alot for this great explanation. I was just thinking of something like that and, I'm working on it .
Your reply kind of make sense. Actually, The problem seems clear but I don't know why I couldn't see it.
Thanks for the recommendation, I just tend to develop my functions first and clean the code later. ¯_(ツ)_/¯