I am aiming to create a node with multiple selection generated dynamically from an HTTP request

Hi everyone,

This is my first post here and I'm new to NodeRED and to js/HTML in general - I'm usually a backend guy -. I therefore have zero experience so please, forgive me :slight_smile:.

I followed the tutorial on how to create a new node, and I have created several slightly more complicated based on that. Now I'd like to do something more useful even though I realize that matters get a lot more difficult.

What I'd like to do is:

  1. Call an HTTP API - remote server - that returns a JSON.
  2. Being able to have that JSON available in the HTML file, in order to generate the menu I want using the TypedInput Widget " Select multiple items from a list of options"
  3. Return the list of selected options when the user pushes the "Done" button.

So regarding point 1, I have implemented the HTTP call in the .js file:

module.exports = function(RED) {

    function JsonNode(config) {

        RED.nodes.createNode(this,config);

        const https = require('https')

            const url = "https://test345.free.beeceptor.com/test";

            https.get(url, res => {

                let data = '';

                res.on('data', chunk => {

                    data += chunk;

                });

                res.on('end', () => {

                    data = JSON.parse(data)

                    console.log(data)

                })

            }).on('error', err => {

                console.log(err.message)

            })      

    }

    RED.nodes.registerType("json-ex1",JsonNode);

}

Again, because based on what I found, I think I understood that's the best way to call a remote endpoint.
Then, for point 2, based on what I have seen, I should loop into the result and build the multiselect using the oneditprepare method. So I realize this is a newbie question, but how would I access data retrieved from the previous HTTP call here?
Regarding point 3 instead, I do realize that the function corresponding to the action I mentioned earlier is the oneditsave but again, I could not find an explanation of how to access the multiselect output.

I hope my explanation was clear,
Thank you in advance!

Urm, you are rather jumping straight into a very deep rabbit hole here I think :slight_smile:

The first question I have to ask is: why? On the face of it, Node-RED already has core nodes that will do what you want.

I can understand though if you are doing this just as a learning exercise. But you chosen something fairly complex to jump into as a starter project.

Without looking in detail at your code, I'd guess that you are falling foul of JavaScript's async complexities. With things not happening in the order you expect them to. Because you are going into an async function.

Yes I do recognize that. The point is that I'd need this custom node to fit into something else which uses the output of the selection, and I'm sure this can be achieved with core nodes - done that using an injected sample -.

But since you say that this one could be achieved with core nodes, how could I? I mean, I could do the call with an HTTP request node, but then I'd go back to the original problem (i.e., having such payload available in the HTML file of the custom node) :slight_smile:.

Is there a resource you recommend where I can fill my js/HTML gaps?

Thanks a lot

This should be done Client Side (though you can do it server side if you must)

client side

  1. Add a variable in the defaults for storing the users selection
  2. On oneditprepare call to your endpoint (using $.ajax)
    1. in the response, populate your list (selecting any previously selected items)
  3. On oneditsave store the users selection in the node variable

Hey Steve,

Thanks for your answer. I read somewhere - might be horribly wrong - that ajax calls on the HTML file are done to local resources. In my case, the endpoint would be remote, and I'm using a mock server atm to simulate it.

Thanks a lot

the html file ajax calls are typically to the node-red server - so that is often local but may be a different box to where the browser/editor is being viewed. there i s nothing to stop you pointing it somewhere else - but it would be a brave node that pointed to an external 3rd party server and expect it to be there all the time. May be ok for a private node where for example you are pointing to some company resource - but probably not a good idea for a node you intend to be public.

Based on what you say I believe it would be better if I did the request using the core HTTP request node, and pass the response to my custom. But in this case, should I access the response payload again using ajax in the HTML file? If so, is there a resource that indicates how to access node-scoped variables?

I hope this was clear,
Thanks!

Where you put the HTTP Request is depending on what you are doing.

for example, if you hard code an internet address in the server-side code - then the node-red server MUST have internet access - something many node-red installations do not have (for security reasons).

Conversely, if the endpoint is called from client-side, more often than not, the user and his browser does have internet access.

Like i said, it depends on what you are attempting to achieve.

Where is this other resource? Is the URL settable by the user on the client side?

Hey Steve,

In the real use case that would derive from this, I'd expect the call to be localhost/something. But for the sake of testing and understanding, I was using a remote HTTP mock server right now.

Based on what you say though, I'd prefer the call to be on the client side. Again, both is this use case and in the real one I'm thinking of, I'd expect the URL to be fixed.

well localhost will be local to the browser - so you may not actually want that in the general case... - or you may... - but yes nothing to stop you experimenting. It should work either way - but make sure you test the different configuration scenarios before you ship it :slight_smile:

Haha I'm not really gonna ship it, it's a kind of "useless" project even though the use case is real.

So, in this scenario -i.e., keeping the call remote- I believe that having it in the oneditprepare would make sense. The thing here is that when I move the same call that I have done previously in the js file:

oneditprepare: function() {
            const https = require('https')
            const url = "https://test.free.beeceptor.com/tags";
            https.get(url, res => {
                let data = '';
                res.on('data', chunk => {
                    data += chunk;
                });
                res.on('end', () => {
                    data = JSON.parse(data)
                    console.log(data)
                })
            }).on('error', err => {
                console.log(err.message)
            })      
        }

I get this error when clicking on the Node:

ReferenceError: require is not defined
at Object.oneditprepare (:13:27)
at t (red.min.js?v=2.2.0:18:378152)
at E (red.min.js?v=2.2.0:18:379525)
at Object.open (red.min.js?v=2.2.0:18:391820)
at i (red.min.js?v=2.2.0:18:503894)
at Object.show (red.min.js?v=2.2.0:18:505105)
at Object.edit (red.min.js?v=2.2.0:18:392449)
at SVGRectElement.at (red.min.js?v=2.2.0:18:209002)
at SVGRectElement.i [as __onmouseup] (vendor.js?v=2.2.0:35:7943)

Does it have to do with the fact that I'm on client side now?

Thanks!

require is a nodejs thing (doesn't work in browser). That's why I said use $.ajax

1 Like

Hey Steve & al.,

Thanks for the support. I managed to do this part (i.e., call to the HTTP endpoint, and building the multiple selection accordingly). Here is the code, as you might have understood I never used HTML and js so :slight_smile:

oneditprepare: function() {
            const url = 'https://casatest.free.beeceptor.com/tags'
            let tags = [];
            let selections = [];
            $.ajax({
                url: url,
                type: "GET",
                success: function(result){
                    tags = result.tags
                    console.log(tags)
                    for (let i = 0; i < tags.length; i++){
                        let obj = {value: tags[i], label: tags[i]}
                        selections.push(obj)
                    }
                    $("#node-input-tags").typedInput({type:"tags", types:[{
                        value: "tags",
                        multiple: true,
                        options: selections
                    }]})
                },
                error: function(result){
                    console.log('Error ${error}')
                }
            })
        }

Now the piece that I'm missing is: how do I access the selections on the oneditsave method? I don't think I found the right reference anywhere.

Thanks a lot!

Use the 'value' function as described in the docs

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