Here's my example.
The configuration of my node consists of a 3 level hierarchy:
- Server: which is the connection to the server hosting the application on which I want to call the API. The configuration of the server consists of address and username/psw. This is a configuration node.
- then Plant: the server application provide the possibility to deploy several plant hosted on the same server.
- then Application: a Server will contain a list of deployed application per plant (I have a metadata url endpoint to retrieve the list of application on the server, given a specified plant.)
- then Command: each application host a list of command. I can get the list of deployed command using a metadata call again.
What I want to achieve is that once the Server parameter is configured, the list of application will be retrieved, and once an application is selected the list of command will be retrieved.
I'm able to do so, but only at the first "selection": If I change the selected Server, the list of application will not change. It looks like the dropdown for application can be drawn only once.
Same goes for Application and Commands.
Here's my node js/html, there might be still some trying I made in the code, but the problem persists:
server.html
RED.nodes.registerType('server',{
category: 'config',
defaults: {
name: {value:"localhost",required:true},
serverName: {value:"localhost",required:true},
username: {value:"username",required:true},
password: {value:"password",required:true}
},
label: function() {
return this.name+" ("+this.username+")";
}
});
</script>
<script type="text/html" data-template-name="server">
<div class="form-row">
<label for="node-config-input-name"><i class="fa fa-bookmark"></i> Name</label>
<input type="text" id="node-config-input-name" />
</div>
<div class="form-row">
<label for="node-config-input-serverName"><i class="fa fa-bookmark"></i> Base URL</label>
<input type="text" id="node-config-input-serverName" />
</div>
<div class="form-row">
<label for="node-config-input-username"><i class="fa fa-bookmark"></i> Username</label>
<input type="text" id="node-config-input-username" />
</div>
<div class="form-row">
<label for="node-config-input-password"><i class="fa fa-bookmark"></i> Password</label>
<input type="text" id="node-config-input-password" />
</div>
</script>
server.js
module.exports = function(RED) {
function ServerNode(n) {
RED.nodes.createNode(this,n);
this.serverName = n.serverName;
this.username = n.username;
this.password = n.password;
}
RED.nodes.registerType("server",ServerNode);
}
command.html
<script type="text/javascript">
RED.nodes.registerType('command',{
category: 'function',
color: '#a6bbcf',
defaults: {
name: {value:""},
server: {value:"",required:true,type:"server"},
plant: {value:""},
application: {value: ""},
command: {value: ""}
},
inputs: 1,
outputs: 1,
icon: "file.svg",
label: function() {
return this.name||"Command";
},
oneditprepare: function() {
var serverField = $('#node-input-server');
var plantField = $('#node-input-plant');
var applicationField = $('#node-input-application');
var configMetadata;
plantField.prop("disabled", true);
applicationField.prop("disabled", true);
serverField.change(function() {
var serverVal = RED.nodes.node($('#node-input-server').val());
if (serverVal === undefined)
{
configMetadata = null;
setPlant(false);
}
else
{
getConfigMetadata(serverVal).then(function (res){
configMetadata = res;
setPlant(true);
});
}
});
plantField.change(function() {
var plantVal = $('#node-input-plant').val();
if (plantVal === undefined || plantVal == "" || plantVal === null)
{
setApplication(false);
}
else
{
setApplication(true);
}
});
var setPlant = function(enabled) {
Plant = $('#node-input-plant');
if (!enabled) {
Plant.prop('disabled',true);
}
else {
Plant.prop('disabled',false);
const plants = Object.keys(configMetadata.plants);
Plant.typedInput({ type: 'plant', types: [{ value: 'plant', options: plants }] });
}
}
var setApplication = function(enabled) {
Application = $('#node-input-application');
if (!enabled)
{
Application.prop('disabled',true);
}
else
{
Application.prop('disabled',false);
opcPlant = $('#node-input-plant').val();
const plant = configMetadata.plants[opcPlant];
const apps = Object.keys(plant.applicationServiceUrls);
Application.typedInput({ types: [{ options: apps }] });
}
}
var setCommand = function(enabled) {
Command = $('#node-input-command');
}
var getConfigMetadata = async function(server) {
var baseUrl = "https://"+server.serverName+"/config";
const options = {method: 'GET', headers: {Accept: 'application/xml, text/plain, */*'}};
try {
const response = await fetch(baseUrl, options);
const data = await response.json();
console.log(data);
return data;
} catch (error) {
console.error(error);
}
};
}
});
</script>
<script type="text/html" data-template-name="command">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label for="node-input-server"><i class="fa fa-tag"></i>Server</label>
<input type="text" id="node-input-server">
</div>
<div class="form-row">
<label for="node-input-plant"><i class="fa fa-tag"></i> Plant</label>
<input type="text" id="node-input-plant">
</div>
<div class="form-row">
<label for="node-inputapplication"><i class="fa fa-tag"></i> Application</label>
<input type="text" id="node-input-application">
</div>
<div class="form-row">
<label for="node-input-command"><i class="fa fa-tag"></i> Command</label>
<input type="text" id="node-input-command">
</div>
</script>
command.js
module.exports = function(RED) {
function CommandNode(config) {
RED.nodes.createNode(this,config);
var node = this;
node.on('input', function(msg) {
msg.payload = msg.payload.toLowerCase();
node.send(msg);
});
}
RED.nodes.registerType("command",CommandNode);
}