Uibuilder multiple button (Button only work once!)

I am working with uibuilder for last few days.
I have created a simple flow like this where there is a form and some button :

[{"id":"e4695ab2.c1ac08","type":"inject","z":"99307f08.810f6","name":"","topic":"cardId","payload":"111","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":300,"y":240,"wires":[["240a7199.dca9fe"]]},{"id":"dfe7b83f.29fe78","type":"inject","z":"99307f08.810f6","name":"","topic":"cardId","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":320,"y":280,"wires":[["240a7199.dca9fe"]]},{"id":"761dae9b.b0861","type":"inject","z":"99307f08.810f6","name":"","topic":"cardId","payload":"ABCD","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":310,"y":320,"wires":[["240a7199.dca9fe"]]},{"id":"240a7199.dca9fe","type":"uibuilder","z":"99307f08.810f6","name":"","topic":"","url":"feedttt","fwdInMessages":true,"allowScripts":false,"allowStyles":false,"copyIndex":true,"showfolder":true,"x":530,"y":280,"wires":[["6119f142.2c665"],[]]},{"id":"6119f142.2c665","type":"debug","z":"99307f08.810f6","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":760,"y":240,"wires":[]}]

HTML:

<!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 rel="stylesheet" href="./index.css" media="all">
</head>
<body>
    <div class="container">
    <div class = "row" id="app">
        <div class = "col-sm-6">
    <div id="app" v-cloak>
        <b-container id="app_container">
            <h2>User Input</h2>
            <b-form class="border p-3 m-2">
                    <b-form-input v-model="buyerId" type="text" placeholder="Enter Buyer ID"></b-form-input><br>
                    <b-form-input v-model="buyerName" type="text" placeholder="Enter Buyer Name"></b-form-input><br>
                    <b-form-input v-model="color" type="text" placeholder="Enter Color"></b-form-input><br>
                
                <div class="row">
				  <div class="form-group col-md-6">
					<div class="form-group">
					    <label for="name">Small Size Ratio</label>
					  <select name="name" v-model="small" class ="selectpicker form-control" rel="stylesheet">
					        <option selected>Small Size Ratio</option>
							<option value="1">1</option>
							<option value="2">2</option>
							<option value="3">3</option>
							<option value="4">4</option>
						</select> 
					</div>
				  </div>
				<div class="form-group col-md-6">
					<div class="form-group">
					<label for="name">Medium Size Ratio</label>
					  <select name="name" v-model="medium" class ="selectpicker form-control" rel="stylesheet">
							<option selected>Medium Size Ratio</option>
							<option value="1">1</option>
							<option value="2">2</option>
							<option value="3">3</option>
							<option value="4">4</option>
						</select> 
				  </div>
				</div>
				</div>
				<div class="row">
				  <div class="form-group col-md-6">
					<div class="form-group">
					    <label for="name">Large Size Ratio</label>
					  <select name="name" v-model="large" class ="selectpicker form-control" rel="stylesheet">
					        <option selected>Large Size Ratio</option>
							<option value="1">1</option>
							<option value="2">2</option>
							<option value="3">3</option>
							<option value="4">4</option>
						</select> 
					</div>
				  </div>
				<div class="form-group col-md-6">
					<div class="form-group">
					<label for="name">XL Size Ratio</label>
					  <select name="name" v-model="xl" class ="selectpicker form-control" rel="stylesheet">
							<option selected>XL Size Ratio</option>
							<option value="1">1</option>
							<option value="2">2</option>
							<option value="3">3</option>
							<option value="4">4</option>
						</select> 
				  </div>
				</div>
				</div>
                    <b-form-input v-model="ply" type="text" placeholder="Enter Number of Ply"></b-form-input><br>
                    <b-form-input v-model="bundleSize" type="text" placeholder="Enter Bundle Size"></b-form-input><br>
                <div class="row">
				  <div class="col-sm-6">
					<div class="form-group">
					    <label for="name">Number of Operation</label>
					  <select name="name" v-model="opId" class ="selectpicker form-control" rel="stylesheet">
					        <option selected>Number of Operation</option>
							<option value="1">1</option>
							<option value="2">2</option>
							<option value="3">3</option>
							<option value="4">4</option>
						</select> 
					</div>
				  </div>
				</div>
				
                    <b-button pill variant="primary" v-on:click="increment">Submit</b-button>
                        &nbsp;&nbsp;Click Counter: <b>{{counterBtn}}</b>.
                    
            </b-form>


            <b-card class="mt-3" header="Log Messages" border-variant="primary" header-bg-variant="primary" header-text-variant="white" align="left" >
                <p>
                    Messages: Received=<b>{{msgsReceived}}</b>, Sent=<b>{{msgsSent}}</b>
                    
                </p>
                
                <pre v-html="hLastRcvd" class="syntax-highlight"></pre>
                <pre v-html="hLastSent" class="syntax-highlight"></pre>
            </b-card>
        </b-container>
        </div>
    </div>
    
    <div class = "col-md-6">
         <div id="app" v-cloak>
        <b-container id="app_container">
       <p> Cart Insertion Section</p>
        <div>Current Card No: {{cardId}}</div>
  
  <div role="tablist">
    <b-card no-body class="mb-1">
      <b-card-header header-tag="header" class="p-1" role="tab">
        <b-button block href="#" v-b-toggle.accordion-1 variant="info">Small Card Input.Remaining: {{cardId}} </b-button>
      </b-card-header>
      <b-collapse id="accordion-1" visible accordion="my-accordion" role="tabpanel">
        <b-card-body>
          <b-card-text>I start opened because <code>visible</code> is <code>true</code></b-card-text>
          <b-card-text>{{cardId}}</b-card-text>
           <b-button pill variant="primary" v-on:click="smallCardSubmit">Submit</b-button>
        </b-card-body>
      </b-collapse>
    </b-card>

    <b-card no-body class="mb-1">
      <b-card-header header-tag="header" class="p-1" role="tab">
        <b-button block href="#" v-b-toggle.accordion-2 variant="info">Medium Card Input.Remaining: {{cardId}}</b-button>
      </b-card-header>
      <b-collapse id="accordion-2" accordion="my-accordion" role="tabpanel">
        <b-card-body>
          <b-card-text>{{cardId}}</b-card-text>
           <b-button pill variant="primary" v-on:click="mediumCardSubmit">Submit</b-button>
        </b-card-body>
      </b-collapse>
    </b-card>

    <b-card no-body class="mb-1">
      <b-card-header header-tag="header" class="p-1" role="tab">
        <b-button block href="#" v-b-toggle.accordion-3 variant="info">Large Card Input.Remaining: {{cardId}}</b-button>
      </b-card-header>
      <b-collapse id="accordion-3" accordion="my-accordion" role="tabpanel">
        <b-card-body>
          <b-card-text>{{cardId}}</b-card-text>
           <b-button pill variant="primary" v-on:click="largeCardSubmit">Submit</b-button>
        </b-card-body>
      </b-collapse>
    </b-card>
        <b-card no-body class="mb-1">
      <b-card-header header-tag="header" class="p-1" role="tab">
        <b-button block href="#" v-b-toggle.accordion-4 variant="info">XL Card Input.Remaining: {{cardId}}</b-button>
      </b-card-header>
      <b-collapse id="accordion-4" accordion="my-accordion" role="tabpanel">
        <b-card-body>
          <b-card-text>{{cardId}}</b-card-text>
           <b-button pill variant="primary" v-on:click="xlCardSubmit">Submit</b-button>
        </b-card-body>
      </b-collapse>
    </b-card>
  </div>
				  
		        </b-container>
        </div>		
        </div>
         
</div>
        
        
</div>
    <script src="../uibuilder/vendor/socket.io/socket.io.js"></script>

    <!-- Vendor Libraries - Load in the right order -->
    <script src="../uibuilder/vendor/vue/dist/vue.js"></script> <!-- dev version with component compiler -->
    <!-- <script src="../uibuilder/vendor/vue/dist/vue.min.js"></script>   prod version with component compiler -->
    <!-- <script src="../uibuilder/vendor/vue/dist/vue.runtime.min.js"></script>   prod version without component compiler -->
    <script src="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.js"></script>

    <!-- REQUIRED: Sets up Socket listeners and the msg object -->
    <!-- <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>

JS:

/* 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   : '',
        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,
    }, // --- End of data --- //
    computed: {
        
        hLastRcvd: function() {
            var msgRecvd = this.msgRecvd
            if (typeof msgRecvd === 'string') return 'Last Message Received = ' + msgRecvd
            else return 'Last Message Received = ' + this.syntaxHighlight(msgRecvd)
        },
        hLastSent: function() {
            var msgSent = this.msgSent
            if (typeof msgSent === 'string') return 'Last Message Sent = ' + msgSent
            else return 'Last Message Sent = ' + this.syntaxHighlight(msgSent)
        },
        hLastCtrlRcvd: function() {
            var msgCtrl = this.msgCtrl
            if (typeof msgCtrl === 'string') return 'Last Control Message Received = ' + msgCtrl
            else return 'Last Control Message Received = ' + this.syntaxHighlight(msgCtrl)
        },
        hLastCtrlSent: function() {
            var msgCtrlSent = this.msgCtrlSent
            if (typeof msgCtrlSent === 'string') return 'Last Control Message Sent = ' + msgCtrlSent
            //else return 'Last Message Sent = ' + this.callMethod('syntaxHighlight', [msgCtrlSent])
            else return 'Last Control Message Sent = ' + this.syntaxHighlight(msgCtrlSent)
        },
    }, // --- End of computed --- //
    methods: {
        increment: function() {
            // Increment the count by one
            this.counterBtn = this.counterBtn + 1
            var topic = this.msgRecvd.topic || 'uibuilder/vue'
            uibuilder.send( {
                'topic': topic,
                'payload': {
                    'type': 'counterBtn',
                    'btnCount': this.counterBtn,
                    'buyerId': this.buyerId,
                    'buyerName': this.buyerName,
                    'color': this.color,
                    'small': this.small,
                    'medium': this.medium,
                    'large': this.large,
                    'xl': this.xl,
                    'ply': this.ply,
                    'bundleSize': this.bundleSize,
                    'opId': this.opId
                }
            } )
        }, // --- End of increment --- //
        smallCardSubmit: function() {
            // Increment the count by one
            this.smallCardSubmit = this.smallCardSubmit + 1
            var topic = this.msgRecvd.topic || 'uibuilder/vue'
            uibuilder.send( {
                'topic': topic,
                'payload': {
                    'type': 'smallCardSubmit',
                    'sbtnCount': this.smallCardSubmit,
                    'button': '1'
                }
            } )
        }, // --- End of increment --- //
        mediumCardSubmit: function() {
            // Increment the count by one
            this.mediumCardSubmit = this.mediumCardSubmit + 1
            var topic = this.msgRecvd.topic || 'uibuilder/vue'
            uibuilder.send( {
                'topic': topic,
                'payload': {
                    'type': 'mediumCardSubmit',
                    'mbtnCount': this.mediumCardSubmit,
                    'button': '2'
                }
            } )
        }, // --- End of increment --- //
        largeCardSubmit: function() {
            // Increment the count by one
            this.largeCardSubmit = this.largeCardSubmit + 1
            var topic = this.msgRecvd.topic || 'uibuilder/vue'
            uibuilder.send( {
                'topic': topic,
                'payload': {
                    'type': 'largeCardSubmit',
                    'lbtnCount': this.largeCardSubmit,
                    'button': '3'
                }
            } )
        }, // --- End of increment --- //
        xlCardSubmit: function() {
            // Increment the count by one
            this.xlCardSubmit = this.xlCardSubmit + 1
            var topic = this.msgRecvd.topic || 'uibuilder/vue'
            uibuilder.send( {
                'topic': topic,
                'payload': {
                    'type': 'xlCardSubmit',
                    'xlbtnCount': this.xlCardSubmit,
                    'button': '4'
                }
            } )
        }, // --- End of increment --- //

        // 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: init,mounted,updated,destroyed
    mounted: function(){
        //console.debug('[indexjs:Vue.mounted] app mounted - setting up uibuilder watchers')

        /** **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('/nr/uib', '/nr/uibuilder/vendor/socket.io') // change to use your paths/names
         */
        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.info('[indexjs:uibuilder.onChange] msg received from Node-RED server:', newVal)
            vueApp.msgRecvd = newVal
                if ( newVal.topic == 'cardId' ) {
        vueApp.cardId = newVal.payload
        }
                        if ( newVal.topic == 'smallCount' ) {
        vueApp.smallCount = newVal.payload
        }
                        if ( newVal.topic == 'MedCount' ) {
        vueApp.MedCount = newVal.payload
        }
                        if ( newVal.topic == 'largeCount' ) {
        vueApp.largeCount = newVal.payload
        }
                        if ( newVal.topic == 'xlCount' ) {
        vueApp.xlCount = newVal.payload
        }
        })
        // As we receive new messages, we get an updated count as well
        uibuilder.onChange('msgsReceived', function(newVal){
            //console.info('[indexjs:uibuilder.onChange] Updated count of received msgs:', newVal)
            vueApp.msgsReceived = newVal
        })

        // 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(newVal){
            //console.info('[indexjs:uibuilder.onChange:ctrlMsg] CONTROL msg received from Node-RED server:', newVal)
            vueApp.msgCtrl = newVal
        })
        // Updated count of control messages received
        uibuilder.onChange('msgsCtrl', function(newVal){
            //console.info('[indexjs:uibuilder.onChange:msgsCtrl] Updated count of received CONTROL msgs:', newVal)
            vueApp.msgsControl = newVal
        })
        //#endregion ---- End of Trace Received Messages ---- //

        //#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
        })

        // If we send a control message to Node-RED, we can get a copy of it here
        uibuilder.onChange('sentCtrlMsg', function(newVal){
            //console.info('[indexjs:uibuilder.onChange:sentCtrlMsg] Control message sent to Node-RED server:', newVal)
            vueApp.msgCtrlSent = newVal
        })
        // And we can get an updated count
        uibuilder.onChange('msgsSentCtrl', function(newVal){
            //console.info('[indexjs:uibuilder.onChange:msgsSentCtrl] Updated count of CONTROL msgs sent:', newVal)
            vueApp.msgsCtrlSent = newVal
        })
        //#endregion ---- End of Trace Sent Messages ---- //

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

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

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


  
// EOF

The problem is, submit button is working fine and it's submit form info multiple time. But my other button only trigger one time. After one press, button don't work. But I need to trigger multiple time. Thanks in advance.

What did the web inspector show ?

These (and alike):

vueApp.MedCount

need to be defined in data:{}

I have declared the variables in data:{}
the console log is:

These (and alike):

vueApp.MedCount

need to be defined in data:{}

I have declared the variables in data:{}

I don't see it:

data: {
        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,
    }, // --- End of data --- //
1 Like

I mean after you told me...

    data: {
        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,
        cardId      :0,
        smallCount  :0,
        MedCount    :0,
        largeCount  :0,
        xlCount     :0,
        buyerId     :'[Nothing]',
        small       :0,
        medium      :0,
        large       :0,
        xl          :0,
        buyerName   :'[Nothing]',
        color       :'[Nothing]',
        ply         :0,
        bundleSize  :0,
        opId        :0,
        
    }, // --- End of data --- //

When I click towice, this error comes in consol:

@TotallyInformation
Can you please have a look?

Solved it! Thanks, guys.
My mistake was, I declared the function name and variable name the same. Now it's working fine.


Thanks

For reference, while VueJS errors can be a bit impenetrable, as in this case, they do indicate that you've done something wrong with Vue itself. The usual troubleshooting rules apply, expand the errors in the console to see if it tells you where in your code the error is, lots of console messages to break down where the error comes from, strip out parts of the code one by one to also see where the error is.

1 Like