Tutorial - create a sidebar plugin and persist the data in a config node

Ok - Been following this thread for sometime.

@BartButenaers - Great tutorial :+1:

One thing I find great for sidebar is the use of the stack panels & tree views!
I use them in my ZWave plugin a fair bit

3 Likes

Really nice layout :star_struck:

1 Like

Are you building those by hand or using a library?

I do have a hint for sidebar - which may help others in easing the design
I use https://handlebarsjs.com - to design the skeleton of the layout (1 in a million ways of course)

And use its JSON data processing features to fill in the data placeholders

A Breif example

HTML Template (held in a plugin html file)

<!-- Side Panel Template-->
<!-- prettier-ignore -->
<template id="TPL_SidePanel">
	<!--Network Selector-->
	<div class="red-ui-sidebar-header" id="zwave-js-network-header" style="display: none; text-align: center; height: 20px">
		<i class="fa fa-cog" aria-hidden="true"></i>
		Network/Runtime :
		<select id="zwave-js-network" onchange="NetworkSelected()" style="height: 25px; vertical-align: baseline">
			<option>Select Network</option>
                    {{#each Networks}}
                       <option value="{{this.id}}">{{this.name}}></option>
                    {{/each}}
		</select>
	</div>
</template>

JS - loaded by plugin html (using the resources API)

const Content = $('<div>')
const Template = Handlebars.compile($('#TPL_SidePanel').html());
const Data = {Networks:[{id: 4, name: "Some Network"}]}
Content.append(Template(Data))


RED.sidebar.addTab({
   ....
   content: Content,
   ...		
});

@TotallyInformation
For the stacks I use the Node RED CSS classes, and for the tree views, use the built in methods.

2 Likes

Undoubtedly, you have a better memory than me and probably know the core code better as well - do you have a hint? :slight_smile:

That is interesting because I've started using templates in the html file for uibuilder as well. Just in the uib-element node and they are all fairly blank right now but will be used dynamically to swap the Element Config tab depending on the selected element type:

image

Allowing for different advanced config settings by element type.

2 Likes

I like using templates - to aid in the detachment between the developer and the designer
(but in this case - I'm both :laughing:) - but it helps (a lot) to get focus on the design.

And yes - it nice being able to empty() and re-populate based on a template for the selection

For a tree view, here is an example from my code base.

const TreeData = [];
ZWNodes.forEach((N) => {
    const Label = $('<div>');
    Label.append(`<span class="zwave-js-node-id-list">${N.nodeId}</span> `);
    Label.append(N.nodeName || 'No Name');

    TreeData.push({
        element: Label,
        nodeData: N
    });
});
$('#zwave-js-node-list').treeList('data', TreeData);

Note the custom property nodeData
the element with id zwave-js-node-list is of course in my template html

$('#zwave-js-node-list').on('treelistselect', nodeSelected);
const nodeSelected = (event, item) => {
		 //item.nodeData.nodeName etc etc
}

https://nodered.org/docs/api/ui/treeList/

1 Like

One more question, in this setup for a sidebar panel, where can I setup a http admin post point for retrieving data from the server?

I tried adding the server part to the .js file for the config node:

module.exports = function (RED) {
  function ConfigservercommfromsidebarFunctionality(config) {
    RED.nodes.createNode(this, config)
  }
  RED.nodes.registerType('servercommfromsidebarCfg', ConfigservercommfromsidebarFunctionality);

  console.error( "starting listener") // seen in the server logs

  RED.httpAdmin.post("/servercommfromsidebar",
    RED.auth.needsPermission("servercommfromsidebar.write"),
    (req, res) => {
      console.error("REQUEST RECIEVED")
            res.status(200).send({
              "status": "ok",
            })
    });
}

The starting listener stanza is to be found in the log but the httpAdmin endpoint is 404, not found.

So the idea is that when I open the sidebar and hit the button, it sends a request to the server and then something else happens. Unfortunately the server part isn't working yet.

I have been caught here in the past, I found my error was I was adding a slash at the start in client code

Bad
getJSON("/servercommfromsidebar")

Good
getJSON("servercommfromsidebar")

might be worth checking

2 Likes

yes the slash prevents httpAdminRoot configuration from settings.js having an effect on the frontend request

Note that I have updated my original code above, based on some feedback from @gregorius :+1:

The original sidebar was initialized every time the 'Deploy' button was clicked. Because the wrong 'runtime-state' event handler was removed, e.g. the handler of another sidebar.

The code is now refactored to something like this, to make sure our sidebar event handler function is only called once (i.e. when all the nodes have been loaded):

var initializeSidebar = function( ) { 
   // Remove the event handler, to avoid that the sidebar is initialized at every deploy
   RED.events.off('runtime-state', initializeSidebar); 
    ...
}

// Add your plugin as a new tabsheet in the right sidebar AFTER the flow editor is completely started
RED.events.on('runtime-state', initializeSidebar);

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