UIBuilder: Click button triggers data pulling and then downloading data

Hi there guys!

in ui-builder I would like to download data (e.g. JSON data in Excel format via 'vue-json-excel' module).
So what I've got is a download button which would send a command to the backend which in return sends the JSON-data to the browser front end where that download popup is then offered.

Because the download of static data works like a charm, I would just go right away to the actual problem only where I struggle to create an async call in a function that is triggered when pressing that download button

So, the following code goes here:
in index.html:

...
<b-button id="buttonChangeValue" @click="clickInitiateChangeValue" data-pull="increaseTestValue">Test-Button</b-button>

index.js:

const targetObj = {
    _x: 0,
    get x() {
        return this._x;
    },
    set x(value) {
        this._x = value;
        fulfillPromise();
    }
};

function changeValue() {
    targetObj.x = targetObj.x + 1;
}

let fulfillPromise = function () { // Calling this makes the waitForChange function fulfill the promise it is waiting for, see https://www.py4u.net/discuss/283550
    console.log("Fulfilling promise...");
}; 

function waitForChange() {
    return new Promise(resolve => {
        fulfillPromise = function () { resolve(); }
    });
}

var app1 = new Vue({
    el: '#app',
    data: {
        startMsg: 'Vue has started, waiting for messages',
        ...
    },
    computed: {},
    methods: {
        clickInitiateChangeValue : function(event) {
            console.log('Fetching data... (event Data: ', event)

            //uibuilder.eventSend; // triggers no sending! idk why.
            uibuilder.send( {          // works
                'payload': {
                    'pull': 'increaseTestValue'
                }
            } )

             (async () => {
                while (true) { // The loop is not for pooling. It receives the change event passively.
                    await waitForChange(); // Wait until targetObj.x has been changed.
                    alert(targetObj.x); // Display only when targetObj.x is changed.
                    console.log("targetObj.x is" + targetObj.x);
                    return "1;2;3"; // later on JSON data
                }
            })();
        },
       ...
    },

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

Although the uibuilder.send( { ... } ) sends a signal back to Node Red where my flow receives it, the browser complains that it doesn't know this function:

image

If I move the uibuilder.send(...) block into the (async () => { ... }) (); it doesn't know console.log() or the uibuilder.send() methods. I'm guessing it's something to do with the scope.

TypeError: console.log(...) is not a function
    at wn.clickInitiateChangeValue (index.js:753)
    at He (vue.min.js:6)
    at HTMLButtonElement.n (vue.min.js:6)
    at HTMLButtonElement.Yr.o._wrapper (vue.min.js:6)

Here's the small flow with a simple node that receives the signal from the FE and signals back to it. For now, it's just that simple to see that it works, later on it should send a precomputed JSON data.

So the question is:
How would you implement an asynchronous function (triggered by a button) that fetches the JSON data" routine and once done it provides that data for download (or in my case offer it as Excel via that json-excel module)?

Would appreciate any creative ideas! :slight_smile:

Cheers,
Marcel

clickInitiateChangeValue : async function(event) {
            console.log('Fetching data... (Event Data: ', event, ')');
            uibuilder.send( {
                'topic': "test",
                'payload': {
                    'pull': 'increaseTestValue'
                }
            } )

            while (true) { // The loop is not for pooling. It receives the change event passively.
                await waitForChange(); // Wait until targetObj.x has been changed.
                //alert(targetObj.x); // Show the dialog only when targetObj.x is changed.
                console.log("targetObj.x is" + targetObj.x);
                return "1;2;3";
            }
        },

Moving the async to the function 'clickInitiateChangeValue' fixes the whole problem.

Hi, I wouldn't do it that way at all :slight_smile:

When you send a message back to node-red, you don't need to wait for a response in the same function. So you can do away with the horribly complex promises altogether.

All you need is the standard uibuilder.onChange('msg', function(msg) {...})

In there is where you monitor for the data coming FROM node-red. So as long as there is something in the returned message that identifies it, that is all you need - a simple if statement that wraps the actions for that message.

If you need to, you could add some kind of identifier to the send that your flow replays in the returned data. But most likely you won't need that.

2 Likes

Thanks kindly for your help! Much appreciated. Works like a charm :slight_smile:
Cheers,
Marcel

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