diff --git a/packages/node_modules/@node-red/nodes/core/function/15-change.html b/packages/node_modules/@node-red/nodes/core/function/15-change.html
index 13f6503ae..453752f71 100644
--- a/packages/node_modules/@node-red/nodes/core/function/15-change.html
+++ b/packages/node_modules/@node-red/nodes/core/function/15-change.html
@@ -109,9 +109,41 @@
var del = this._("change.action.delete");
var move = this._("change.action.move");
var to = this._("change.action.to");
+ var toValueLabel = this._("change.action.toValue",to);
var search = this._("change.action.search");
var replace = this._("change.action.replace");
var regex = this._("change.label.regex");
+ var deepCopyLabel = this._("change.label.deepCopy");
+
+ function createPropertyValue(row2_1,row2_2) {
+ var propValInput = $('',{class:"node-input-rule-property-value",type:"text"})
+ .appendTo(row2_1)
+ .typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','bin','date','jsonata','env']});
+
+ var dcLabel = $('').appendTo(row2_2);
+ var deepCopy = $('').appendTo(dcLabel)
+ $('').text(deepCopyLabel).appendTo(dcLabel)
+
+ propValInput.on("change", function(evt,type,val) {
+ row2_2.toggle(type === "msg" || type === "flow" || type === "global" || type === "env");
+ })
+ return [propValInput, deepCopy];
+ }
+ function createFromValue(row3_1) {
+ return $('',{class:"node-input-rule-property-search-value",type:"text"})
+ .appendTo(row3_1)
+ .typedInput({default:'str',types:['msg','flow','global','str','re','num','bool','env']});
+ }
+ function createToValue(row3_2) {
+ return $('',{class:"node-input-rule-property-replace-value",type:"text"})
+ .appendTo(row3_2)
+ .typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','bin','env']});
+ }
+ function createMoveValue(row4) {
+ return $('',{class:"node-input-rule-property-move-value",type:"text"})
+ .appendTo(row4)
+ .typedInput({default:'msg',types:['msg','flow','global']});
+ }
$('#node-input-rule-container').css('min-height','150px').css('min-width','450px').editableList({
addItem: function(container,i,opt) {
@@ -139,10 +171,10 @@
whiteSpace: 'nowrap'
});
let fragment = document.createDocumentFragment();
- var row1 = $('',{style:"display:flex;"}).appendTo(fragment);
- var row2 = $('',{style:"display:flex;margin-top:8px;"}).appendTo(fragment);
+ var row1 = $('',{style:"display:flex; align-items: baseline"}).appendTo(fragment);
+ var row2 = $('',{style:"margin-top:8px;"}).appendTo(fragment);
var row3 = $('',{style:"margin-top:8px;"}).appendTo(fragment);
- var row4 = $('',{style:"display:flex;margin-top:8px;"}).appendTo(fragment);
+ var row4 = $('',{style:"display:flex;margin-top:8px;align-items: baseline"}).appendTo(fragment);
var selectField = $('',{class:"node-input-rule-type",style:"width:110px; margin-right:10px;"}).appendTo(row1);
var selectOptions = [{v:"set",l:set},{v:"change",l:change},{v:"delete",l:del},{v:"move",l:move}];
@@ -154,48 +186,27 @@
.appendTo(row1)
.typedInput({types:['msg','flow','global']});
+ var row2_1 = $('', {style:"display:flex;align-items: baseline"}).appendTo(row2);
$('',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
- .text(to)
- .appendTo(row2);
+ .text(toValueLabel)
+ .appendTo(row2_1);
- function createPropertyValue() {
- return $('',{class:"node-input-rule-property-value",type:"text"})
- .appendTo(row2)
- .typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','bin','date','jsonata','env']});
- }
+ var row2_2 = $('', {style:"margin-top: 4px;"}).appendTo(row2);
- var row3_1 = $('', {style:"display:flex;"}).appendTo(row3);
+ var row3_1 = $('', {style:"display:flex;align-items: baseline"}).appendTo(row3);
$('',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
.text(search)
.appendTo(row3_1);
- function createFromValue() {
- return $('',{class:"node-input-rule-property-search-value",type:"text"})
- .appendTo(row3_1)
- .typedInput({default:'str',types:['msg','flow','global','str','re','num','bool','env']});
- }
-
- var row3_2 = $('',{style:"display:flex;margin-top:8px;"}).appendTo(row3);
+ var row3_2 = $('',{style:"display:flex;margin-top:8px;align-items: baseline"}).appendTo(row3);
$('',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
.text(replace)
.appendTo(row3_2);
- function createToValue() {
- return $('',{class:"node-input-rule-property-replace-value",type:"text"})
- .appendTo(row3_2)
- .typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','bin','env']});
- }
-
$('',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
.text(to)
.appendTo(row4);
- function createMoveValue() {
- return $('',{class:"node-input-rule-property-move-value",type:"text"})
- .appendTo(row4)
- .typedInput({default:'msg',types:['msg','flow','global']});
- }
-
let propertyValue = null;
let fromValue = null;
let toValue = null;
@@ -218,7 +229,9 @@
if (type == "set") {
if(!propertyValue) {
- propertyValue = createPropertyValue();
+ var parts = createPropertyValue(row2_1, row2_2);
+ propertyValue = parts[0];
+ deepCopy = parts[1];
}
propertyValue.typedInput('show');
row2.show();
@@ -226,11 +239,11 @@
row4.hide();
} else if (type == "change") {
if(!fromValue) {
- fromValue = createFromValue();
+ fromValue = createFromValue(row3_1);
}
fromValue.typedInput('show');
if(!toValue) {
- toValue = createToValue();
+ toValue = createToValue(row3_2);
}
toValue.typedInput('show');
row2.hide();
@@ -242,7 +255,7 @@
row4.hide();
} else if (type == "move") {
if(!moveValue) {
- moveValue = createMoveValue();
+ moveValue = createMoveValue(row4);
}
moveValue.typedInput('show');
row2.hide();
@@ -256,26 +269,29 @@
propertyName.typedInput('type',rule.pt);
if (rule.t == "set") {
if(!propertyValue) {
- propertyValue = createPropertyValue();
+ var parts = createPropertyValue(row2_1, row2_2);
+ propertyValue = parts[0];
+ deepCopy = parts[1];
}
propertyValue.typedInput('value',rule.to);
propertyValue.typedInput('type',rule.tot);
+ deepCopy.prop("checked", !!rule.dc);
}
if (rule.t == "move") {
if(!moveValue) {
- moveValue = createMoveValue();
+ moveValue = createMoveValue(row4);
}
moveValue.typedInput('value',rule.to);
moveValue.typedInput('type',rule.tot);
}
if (rule.t == "change") {
if(!fromValue) {
- fromValue = createFromValue();
+ fromValue = createFromValue(row3_1);
}
fromValue.typedInput('value',rule.from);
fromValue.typedInput('type',rule.fromt);
if (!toValue) {
- toValue = createToValue();
+ toValue = createToValue(row3_2);
}
toValue.typedInput('value',rule.to);
toValue.typedInput('type',rule.tot);
@@ -331,6 +347,9 @@
if (type === "set") {
r.to = rule.find(".node-input-rule-property-value").typedInput('value');
r.tot = rule.find(".node-input-rule-property-value").typedInput('type');
+ if (rule.find(".node-input-rule-property-deepCopy").prop("checked")) {
+ r.dc = true;
+ }
} else if (type === "move") {
r.to = rule.find(".node-input-rule-property-move-value").typedInput('value');
r.tot = rule.find(".node-input-rule-property-move-value").typedInput('type');
diff --git a/packages/node_modules/@node-red/nodes/core/function/15-change.js b/packages/node_modules/@node-red/nodes/core/function/15-change.js
index 55b9f44e9..d177caec8 100644
--- a/packages/node_modules/@node-red/nodes/core/function/15-change.js
+++ b/packages/node_modules/@node-red/nodes/core/function/15-change.js
@@ -206,6 +206,9 @@ module.exports = function(RED) {
node.error(err, msg);
return done(undefined,null);
} else {
+ if (rule.dc) {
+ value = RED.util.cloneMessage(value);
+ }
getFromValue(msg,rule,(err,fromParts) => {
if (err) {
node.error(err, msg);
diff --git a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json
index cbb344ed3..a20d7b8db 100755
--- a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json
+++ b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json
@@ -713,13 +713,15 @@
"delete": "delete __property__",
"move": "move __property__",
"changeCount": "change: __count__ rules",
- "regex": "Use regular expressions"
+ "regex": "Use regular expressions",
+ "deepCopy": "Deep copy value"
},
"action": {
"set": "Set",
"change": "Change",
"delete": "Delete",
"move": "Move",
+ "toValue": "to the value",
"to": "to",
"search": "Search for",
"replace": "Replace with"
diff --git a/test/nodes/core/function/15-change_spec.js b/test/nodes/core/function/15-change_spec.js
index 06d1e3774..b8d7be03b 100644
--- a/test/nodes/core/function/15-change_spec.js
+++ b/test/nodes/core/function/15-change_spec.js
@@ -98,7 +98,7 @@ describe('change Node', function() {
});
describe('#set' , function() {
-
+
it('sets the value of the message property', function(done) {
var flow = [{"id":"changeNode1","type":"change","action":"replace","property":"payload","from":"","to":"changed","reg":false,"name":"changeNode","wires":[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
@@ -615,7 +615,6 @@ describe('change Node', function() {
});
-
it('changes the value using jsonata', function(done) {
var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"$length(payload)","tot":"jsonata"}],"name":"changeNode","wires":[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
@@ -847,7 +846,36 @@ describe('change Node', function() {
});
})
+ it('deep copies the property if selected', function(done) {
+ var flow = [{"id":"changeNode1","type":"change","rules":[{"t":"set","p":"payload","pt":"msg","to":"source","tot":"msg","dc":true}],"name":"changeNode","wires":[["helperNode1"]]},
+ {id:"helperNode1", type:"helper", wires:[]}];
+ helper.load(changeNode, flow, function() {
+ var changeNode1 = helper.getNode("changeNode1");
+ var helperNode1 = helper.getNode("helperNode1");
+ helperNode1.on("input", function(msg) {
+ try {
+ // Check payload has been set to a clone of original object
+ // - the JSON should match
+ JSON.stringify(msg.payload).should.equal(JSON.stringify(originalObject))
+ // - but they must be different objects
+ msg.payload.should.not.equal(originalObject);
+
+ // Modify nested property of original object
+ originalObject.a.c = 3;
+ // Check that modification hasn't happened on cloned prop
+ msg.payload.a.should.not.have.property('c');
+
+ done();
+ } catch(err) {
+ done(err);
+ }
+ });
+ var originalObject = { a: { b: 2 } }
+ changeNode1.receive({source:originalObject});
+ });
+
+ })
});
describe('#change', function() {
it('changes the value of the message property', function(done) {