Skip to content

Commit 5cd3ef2

Browse files
authored
Fix renaming of values (#2)
* Fix renaming of configured values
1 parent 1684daf commit 5cd3ef2

10 files changed

+276
-99
lines changed

examples/Collect Multiple Values.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
"name": "",
4747
"valuesConfig": "bdcc10eee2fc3654",
4848
"valuesConfigName": "RoomStates",
49-
"valueSelect": "DoorOpen",
49+
"valueId": "c532de90-a577-11ed-b30d-21918a802e2e",
5050
"value": "DoorOpen",
5151
"command": "write",
5252
"msgProperty": "payload",
@@ -334,13 +334,15 @@
334334
"name": "RoomStates",
335335
"values": [
336336
{
337+
"id": "c532de90-a577-11ed-b30d-21918a802e2e",
337338
"name": "DoorOpen",
338339
"datatype": "bool",
339340
"default": false,
340341
"scope": "global",
341342
"storage": "default"
342343
},
343344
{
345+
"id": "c538aaf0-a577-11ed-b30d-21918a802e2e",
344346
"name": "OpenWindows",
345347
"datatype": "num",
346348
"default": 3,

examples/Dynamic Command Override.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
"name": "",
3232
"valuesConfig": "bbc8971830771b8e",
3333
"valuesConfigName": "Room State",
34-
"valueSelect": "DoorOpen",
34+
"valueId": "a9e87af0-a577-11ed-b30d-21918a802e2e",
3535
"value": "DoorOpen",
3636
"command": "write",
3737
"msgProperty": "payload",
@@ -224,6 +224,7 @@
224224
"name": "Room State",
225225
"values": [
226226
{
227+
"id": "a9e87af0-a577-11ed-b30d-21918a802e2e",
227228
"name": "DoorOpen",
228229
"datatype": "bool",
229230
"default": false,

examples/Read and Write.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"name": "",
3333
"valuesConfig": "7b534a78d263796e",
3434
"valuesConfigName": "House States",
35-
"valueSelect": "LightOn",
35+
"valueId": "6459a040-a577-11ed-b30d-21918a802e2e",
3636
"value": "LightOn",
3737
"command": "write",
3838
"msgProperty": "payload",
@@ -241,20 +241,23 @@
241241
"name": "House States",
242242
"values": [
243243
{
244+
"id": "6459a040-a577-11ed-b30d-21918a802e2e",
244245
"name": "LightOn",
245246
"datatype": "bool",
246247
"default": false,
247248
"scope": "global",
248249
"storage": "default"
249250
},
250251
{
252+
"id": "645fe1d0-a577-11ed-b30d-21918a802e2e",
251253
"name": "OpenWindows",
252254
"datatype": "num",
253255
"default": 0,
254256
"scope": "global",
255257
"storage": "default"
256258
},
257259
{
260+
"id": "6465d540-a577-11ed-b30d-21918a802e2e",
258261
"name": "NextGarbageCollection",
259262
"datatype": "str",
260263
"default": "Monday, 2023-01-08",

nodes/persistent-value.html

Lines changed: 55 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@
4040

4141
defaults: {
4242
name: {value: ''},
43-
valuesConfig: {value: '', type: 'persistent values config'},
44-
valuesConfigName: {required: false},
45-
valueSelect: {required: true},
46-
value: {required: true},
43+
valuesConfig: {value: '', type: 'persistent values config'}, // The selected configuration (ID)
44+
valuesConfigName: {required: false}, // Name of the selected configuration
45+
valueId: {required: false}, // ID of the selected value
46+
value: {required: true}, // Name of the selected value
4747
command: {value: kCommandDefault, required: true},
4848
msgProperty: {value: 'payload', required: false},
4949
collectValues: {value: false, required: false},
@@ -83,8 +83,9 @@
8383
const valuesConfigField = $('#node-input-valuesConfig');
8484
const valuesConfigNameField = $('#node-input-valuesConfigName');
8585

86-
const valueSelectField = $('#node-input-valueSelect');
86+
const valueIdField = $('#node-input-valueId');
8787
const valueField = $('#node-input-value');
88+
const valueSelectField = $('#valueSelect'); // Drop-down
8889

8990
const commandField = $('#node-input-command');
9091
const msgPropertyField = $('#node-input-msgProperty');
@@ -102,44 +103,69 @@
102103
// Initialize all possible types of compare value field. Concrete type dynamically switched based on config.
103104
blockIfCompareValueField.typedInput({type: 'str', types: ['str', 'num', 'bool']});
104105

105-
// Store current configured value name before drop-down repopulation
106-
const currentSelectedValue = valueField.val();
106+
const node = this; // Store access to node configuration itself
107107

108108
valueSelectField.on('change', function() {
109109
const selected = $(this).find('option:selected');
110110

111111
// Ignore re-population change
112112
if (selected.val() !== undefined) {
113-
// Store actual selected value
114-
valueField.val(selected.val());
113+
// Store actual selected value ID and name
114+
const selected_id = selected.data('id');
115+
if(selected_id !== 'undefined') {
116+
node.valueId = selected_id;
117+
valueIdField.val(node.valueId);
118+
}
119+
120+
node.value = selected.val();
121+
valueField.val(node.value);
115122

116123
// Switch datatype of blockIf compare value input field
117124
const selectedDatatype = selected.data('datatype');
118125
blockIfCompareValueField.typedInput('type', selectedDatatype);
119126
blockIfCompareValueField.typedInput('types', [selectedDatatype]);
127+
} else {
128+
node.valueId = undefined;
129+
valueIdField.val(node.valueId);
130+
131+
node.value = selected.val();
132+
valueField.val(node.value);
120133
}
121134
});
122135

123136
// Initially change triggered by editor loaded, later with every user interaction.
124137
valuesConfigField.on('change', async function() {
125138
const selectedConfigId = $(this).val();
126-
backendConfig = await getConfigFromBackend(selectedConfigId);
127-
// Store current selected configuration name
128-
valuesConfigNameField.val(backendConfig.name);
129-
130-
// Re-populate select widget
131-
valueSelectField.empty();
132-
for (const value of backendConfig.values) {
133-
const optionText = `${value.name} [${value.datatype}, ${value.storage}]`;
134-
valueSelectField.append(`<option value="${value.name}" data-datatype="${value.datatype}">${optionText}</option>`);
135-
}
139+
if (selectedConfigId !== '_ADD_') { // Skip if "add new config" is selected
140+
backendConfig = await getConfigFromBackend(selectedConfigId);
141+
// Store current selected configuration name
142+
valuesConfigNameField.val(backendConfig.name);
143+
144+
// Re-populate value select widget
145+
valueSelectField.empty();
146+
for (const value of backendConfig.values) {
147+
const optionText = `${value.name} [${value.datatype}, ${value.storage}]`;
148+
valueSelectField.append(
149+
`<option value="${value.name}" data-id="${value.id}" data-datatype="${value.datatype}">${optionText}</option>`);
150+
}
136151

137-
// Select the previously selected value or set to invalid if the value is not known anymore
138-
if (backendConfig.values.find((value) => value.name === currentSelectedValue)) {
139-
valueSelectField.val(currentSelectedValue).change();
140-
} else {
141-
valueSelectField.val([]);
142-
valueField.val(null);
152+
// Select the previously selected value or set to invalid if the value is not known anymore
153+
if (node.valueId) {
154+
const selectedValue = backendConfig.values.find((value) => value.id === node.valueId);
155+
if (selectedValue) {
156+
$(`#valueSelect option[data-id="${selectedValue.id}"]`).attr('selected', true).trigger('change');
157+
} else {
158+
valueSelectField.val([]).trigger('change');
159+
}
160+
} else {
161+
// Until version 1.1.0 no ID was existing for every value. Therefore search via value name.
162+
const selectedValue = backendConfig.values.find((value) => value.name === node.value);
163+
if (selectedValue) {
164+
valueSelectField.val(selectedValue.name).trigger('change');
165+
} else {
166+
valueSelectField.val([]).trigger('change');
167+
}
168+
}
143169
}
144170
});
145171

@@ -191,10 +217,12 @@
191217
<input type="hidden" id="node-input-valuesConfigName">
192218
</div>
193219
<div class="form-row">
194-
<label for="node-input-valueSelect"><i class="fa fa-adjust"></i> Value</label>
220+
<label for="node-input-value"><i class="fa fa-adjust"></i> Value</label>
195221
<!-- Classic select widget instead of NodeRED TypedInput used due to missing re-population capabiltiy -->
196-
<select id="node-input-valueSelect"></select>
222+
<select id="valueSelect"></select>
197223
<!-- Store the actually selected value in a hidden field. Required due to dynamic drop-down repopulation -->
224+
<input type="hidden" id="node-input-valueId">
225+
<!-- Store the actually selected value name in a hidden field. Required for node label creation -->
198226
<input type="hidden" id="node-input-value">
199227
</div>
200228
<div class="form-row">

nodes/persistent-value.js

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
module.exports = function(RED) {
2+
const uuid = require('uuid');
3+
24
// ---- elements of the reference config ----
35

46
// Datatypes defined by TypedInput widget
@@ -43,7 +45,7 @@ module.exports = function(RED) {
4345
}
4446

4547
function buildNodeStatus(node, msgProperty, blockFlow) {
46-
let storage = node.config.storage;
48+
let storage = node.valueConfig.storage;
4749
if (storage === kStorageDefault) {
4850
if (RED.settings.contextStorage !== undefined && RED.settings.contextStorage.default !== undefined) {
4951
storage += ` (${RED.settings.contextStorage.default})`;
@@ -53,7 +55,7 @@ module.exports = function(RED) {
5355
node.status({
5456
fill: `${blockFlow ? 'red' : 'green'}`,
5557
shape: 'dot',
56-
text: `${msgProperty} [${kSupportedDatatypesByTypedInput[node.config.datatype]},${node.config.scope},${storage}]`,
58+
text: `${msgProperty} [${kSupportedDatatypesByTypedInput[node.valueConfig.datatype]},${node.valueConfig.scope},${storage}]`,
5759
});
5860
}
5961

@@ -81,10 +83,10 @@ module.exports = function(RED) {
8183

8284
function getUsedContext(node) {
8385
let context = node.context();
84-
if (node.config.scope === 'flow') {
86+
if (node.valueConfig.scope === 'flow') {
8587
context = context.flow;
8688
}
87-
if (node.config.scope === 'global') {
89+
if (node.valueConfig.scope === 'global') {
8890
context = context.global;
8991
}
9092
return context;
@@ -98,24 +100,24 @@ module.exports = function(RED) {
98100

99101
function getContext(node, context, contextKey) {
100102
let currentValue = undefined;
101-
if (node.config.storage === kStorageDefault) {
103+
if (node.valueConfig.storage === kStorageDefault) {
102104
currentValue = context.get(contextKey);
103105
} else {
104-
currentValue = context.get(contextKey, node.config.storage);
106+
currentValue = context.get(contextKey, node.valueConfig.storage);
105107
}
106108

107109
// Apply default value if context contains no value
108110
if (currentValue === undefined) {
109-
currentValue = node.config.default;
111+
currentValue = node.valueConfig.default;
110112
}
111113
return currentValue;
112114
}
113115

114116
function setContext(node, context, contextKey, newValue) {
115-
if (node.config.storage === kStorageDefault) {
117+
if (node.valueConfig.storage === kStorageDefault) {
116118
context.set(contextKey, newValue);
117119
} else {
118-
context.set(contextKey, newValue, node.config.storage);
120+
context.set(contextKey, newValue, node.valueConfig.storage);
119121
}
120122
}
121123

@@ -134,7 +136,7 @@ module.exports = function(RED) {
134136

135137
function convertToExpectedType(node, value) {
136138
let convertedValue = undefined;
137-
switch (node.config.datatype) {
139+
switch (node.valueConfig.datatype) {
138140
case kConfigDatatypeBool:
139141
if (value === 'true') {
140142
convertedValue = true;
@@ -156,11 +158,11 @@ module.exports = function(RED) {
156158
}
157159
break;
158160
default:
159-
node.error(`Unsupported compare value type '${node.config.datatype}' configured!`);
161+
node.error(`Unsupported compare value type '${node.valueConfig.datatype}' configured!`);
160162
}
161163

162164
if (convertedValue === undefined) {
163-
node.error(`Failed to convert value '${value}' to expected datatype '${node.config.datatype}'!`);
165+
node.error(`Failed to convert value '${value}' to expected datatype '${node.valueConfig.datatype}'!`);
164166
}
165167

166168
return convertedValue;
@@ -169,7 +171,7 @@ module.exports = function(RED) {
169171
function compareToConfiguredDatatype(node, value) {
170172
const typeOfValue = typeof value;
171173
return kSupportedDatatypesLanguageType.hasOwnProperty(typeOfValue) &&
172-
(kSupportedDatatypesLanguageType[typeOfValue] === node.config.datatype);
174+
(kSupportedDatatypesLanguageType[typeOfValue] === node.valueConfig.datatype);
173175
}
174176

175177
function checkBlockIfCondition(node, currentValue) {
@@ -213,14 +215,28 @@ module.exports = function(RED) {
213215
return null;
214216
}
215217

216-
node.config = configNode.values.find((value) => value.name === nodeConfig.value);
217-
node.configName = configNode.name; // Name of the referenced configuration
218-
node.value = nodeConfig.value; // selected value
219-
if (node.value === '' || node.value === undefined || node.value === null) {
218+
// Selected value by ID or name
219+
node.valueId = nodeConfig.valueId;
220+
node.value = nodeConfig.value;
221+
if (node.valueId !== undefined && !uuid.validate(node.valueId)) {
222+
reportIncorrectConfiguration(node);
223+
return null;
224+
}
225+
// Selectd value must be referenced via ID or name (deprecated)
226+
if (node.valueId === undefined && (node.value === undefined)) {
220227
reportIncorrectConfiguration(node);
221228
return null;
222229
}
223230

231+
node.configName = configNode.name; // Name of the referenced configuration
232+
if (node.valueId) {
233+
node.valueConfig = configNode.values.find((value) => value.id === node.valueId);
234+
node.value = node.valueConfig.name; // Update name again in case of inconsistent value / ID config
235+
} else {
236+
// Until version 1.1.0 no ID was existing for every value. Therefore search via value name.
237+
node.valueConfig = configNode.values.find((value) => value.name === node.value);
238+
}
239+
224240
node.command = nodeConfig.command || kCommandDefault;
225241
node.msgProperty = nodeConfig.msgProperty || kMsgPropertyDefault;
226242

@@ -241,7 +257,7 @@ module.exports = function(RED) {
241257
let currentValue = getContext(node, context, contextKey);
242258
if (!compareToConfiguredDatatype(node, currentValue)) {
243259
node.warn(`Persisted value ${node.configName} / ${node.value} does not have the configured datatype ` +
244-
`'${node.config.datatype}'!`);
260+
`'${node.valueConfig.datatype}'!`);
245261
}
246262

247263
let onChangeMsg = null;
@@ -265,7 +281,7 @@ module.exports = function(RED) {
265281

266282
if (!compareToConfiguredDatatype(node, inputValue)) {
267283
node.error(`Passed value in msg.${node.msgProperty} does not have the configured datatype ` +
268-
`'${node.config.datatype}'! Persistent value config: ${node.configName} / ${node.value}`);
284+
`'${node.valueConfig.datatype}'! Persistent value config: ${node.configName} / ${node.value}`);
269285
return;
270286
}
271287

0 commit comments

Comments
 (0)