Hey Jisu,
I have only create one sidebar node (see node-red-contrib-xterm) until now, and that is already quite some time ago. So I might have forgotten some stuff, but I'm sure others will correct me where needed ...
Some facts about such a sidebar tab:
-
It depends on a single configuration node.
-
Keep in mind that the user can remove that config node without you knowing it.
[EDIT] For example the config node of my xterm terminal sidebar can be found here:
The user can remove the config node there. Or the user can open your config node's config screen (after double clicking on it) and change the data of your config node. And so on ... Just to say that your sidebar panel screen needs to be aware of this, and always be in sync with these config node updates!
-
So you need to ensure that there exist always a single instance of that config node. Which means you have to create that config node instance (if it doesn't exist yet) in the following cases:
- When the flow editor is being opened, i.e. when your node is being added (by Node-RED) to the palette.
- Every time you need to read data from that config node, or to save data into that config node.
-
For a normal node you only have to specify one single data template, which specifies how the (config) node's config screen looks like. However now you will have to specify extra a second data template, to specify how the sidebar panel looks like.
The javascript file looks rather similar to that of a normal node, but the html file is a bit different (based on the list of facts we just described). Below you can find an example of an html file, with lots of explanation inside. This code might contain syntax/logical errors, but it is just to give you an idea of how I think it works:
<script type="text/javascript">
(function ($) {
// Keep a reference to the config node, the last time you have found it ...
var myConfigNode = null;
// Function to ensure that a single instance of myConfigNode (still) exists.
// Call this function always before you try to access the config node!!!!
// Caution: this function cannot be called from onpaletteadd, because then the RED.nodes are not filled yet!!!
function ensureMyConfigNodeExists() {
// If we have found the config node previously, check if it has been deleted by the user meanwhile
if (myConfigNode !== null) {
var configNode = RED.nodes.node(myConfigNode.id);
if (configNode === null) { myConfigNode = null; }
}
// If not found previously, check if it has been created meanwhile
if (myConfigNode === null) {
var configNodes = [];
RED.nodes.eachConfig(function(configNode) {
if (configNode.type === 'my_unique_config_node_name') {
configNodes.push(configNode);
}
});
// Make sure we only have 1 config node
while (configNodes.length > 1) {
var configNode = configNodes.pop();
RED.nodes.remove(configNode.id);
RED.nodes.dirty(true);
}
// When we found a config node, let's use that one
if (configNodes.length === 1) { myConfigNode = configNodes[0]; }
}
// When the config node doesn't exist yet, let's create a new instance of it
if (myConfigNode === null) {
myConfigNode = {
// Ask Node-RED to generate a new unique node id for your new config node
id: RED.nodes.id(),
// Ask Node-RED the type of your node
_def: RED.nodes.getType("my_unique_config_node_name"),
// Specify your node type
type: "my_unique_config_node_name",
// This config node is not used by another node, so it will appear with status "unused" in the "config nodes" sidebar.
// But that is very confusing, since the config node is used by your sidebar screen.
// To solve that set 'hasUsers' to false (see https://github.com/node-red/node-red/blob/master/CHANGELOG.md#0161-maintenance-release).
// Note that the 'hasUsers' needs also to be specified in the RED.nodes.registerType statement!
hasUsers: false,
users: [],
label: function() { return this.name || "My node label"},
name: "My node name",
// Specify here all your config node properties here (with their default values)
// ...
}
// Add the new config node to the collection of Node-RED nodes
RED.nodes.add(myConfigNode);
// Make sure the "Deploy" button becomes active, by letting Node-RED know that something has changed
RED.nodes.dirty(true);
}
}
function sidebarResizeEventHandler() {
// Resize the widgets inside your sidebar screen if required
}
RED.nodes.registerType('my_unique_config_node_name', {
category: 'config',
hasUsers: false,
defaults: {
name: { value: "My default node name" }
// Specify here again all your config node properties (with the same default values as above)
// ...
},
paletteLabel: 'My node palette label',
label: function () {
return this.name;
},
onpaletteremove: function () {
// Remove the sidebar with id = sidebar-mysidebarname (which we have added a few lines further ...)
RED.sidebar.removeTab("sidebar-mysidebarname");
RED.events.off("sidebar:resize", sidebarResizeEventHandler);
},
onpaletteadd: function () {
// The html content of the sidebar page has been specified a a data-template, from where it can be loaded:
var htmlContent = $($('script[type="text/x-red"][data-template-name="my_unique_config_node_name_sidebar"]').i18n().html());
// Add a new "MySidebarName" tabsheet to the right sidebar in the flow editor, where you show the html content
RED.sidebar.addTab({
id: "sidebar-mysidebarname",
label: "mysidebarlabel",
name: "MySidebarName",
content: htmlContent,
closeable: true,
disableOnEdit: true,
// Select here your own FontAwesome icon that needs to be displayed on the tabsheet in the sidepanel
iconClass: "fa fa-terminal"
});
RED.events.on("sidebar:resize", sidebarResizeEventHandler);
// Here you can now initialise and setup your sidebar screen.
// For example suppose that a value is changed in your sidebar panel, you might want to update that value in your config node.
$("#some_element_id").on("change", function() {
// As said before, make sure your config node instance exists!
ensureMyConfigNodeExists();
// myConfigNode will point to your config node instance, so you can start manipulating it ...
myConfigNode.someProperty = "someValue";
})
}
});
})(jQuery);
</script>
<!-- YOUR FIRST DATA TEMPLATE TO DISPLAY THE CONFIG SCREEN WHEN THE CONFIG NODE IS DISPLAYED -->
<script type="text/x-red" data-template-name="my_unique_config_node_name">
<div class="form-row">
<label for="node-config-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-config-input-name" class="xterm-setting" placeholder="Name">
</div>
<!-- Add here a form-row for every input field that needs to be displayed in the sidebar-->
</script>
<!-- YOUR SECOND DATA TEMPLATE TO DISPLAY IN THE RIGHT SIDEBAR -->
<script type="text/x-red" data-template-name="my_unique_config_node_name_sidebar">
<div style="position: relative; height: 100%;">
<div style="position: absolute; top: 1px; bottom: 2px; left: 1px; right: 1px; overflow-y: scroll; padding: 10px;">
<form class="dialog-form">
<!-- Add here the html elements that needs to be displayed inside your sidebar panel -->
</form>
</div>
</div>
</script>
<!-- YOUR INFO PANEL WITH HELP INFORMATION -->
<script type="text/x-red" data-help-name="my_unique_config_node_name">
<p>Define here the html that needs to be displayed in the info sidebar panel.</p>
</script>
Hopefully this makes a bit clear how it works, so you can get started with your development...
Good luck with it!!
Bart