mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Merge pull request #3156 from node-red/change-clone
Add option to deep-clone properties in Change node
This commit is contained in:
		| @@ -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 = $('<input/>',{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 = $('<label style="padding-left: 130px; display: flex; align-items: center "></label>').appendTo(row2_2); | ||||
|                 var deepCopy = $('<input type="checkbox" class="node-input-rule-property-deepCopy" style="width: auto; margin: 0 6px 0 0">').appendTo(dcLabel) | ||||
|                 $('<span>').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 $('<input/>',{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 $('<input/>',{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 $('<input/>',{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 = $('<div/>',{style:"display:flex;"}).appendTo(fragment); | ||||
|                     var row2 = $('<div/>',{style:"display:flex;margin-top:8px;"}).appendTo(fragment); | ||||
|                     var row1 = $('<div/>',{style:"display:flex; align-items: baseline"}).appendTo(fragment); | ||||
|                     var row2 = $('<div/>',{style:"margin-top:8px;"}).appendTo(fragment); | ||||
|                     var row3 = $('<div/>',{style:"margin-top:8px;"}).appendTo(fragment); | ||||
|                     var row4 = $('<div/>',{style:"display:flex;margin-top:8px;"}).appendTo(fragment); | ||||
|                     var row4 = $('<div/>',{style:"display:flex;margin-top:8px;align-items: baseline"}).appendTo(fragment); | ||||
|  | ||||
|                     var selectField = $('<select/>',{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 = $('<div/>', {style:"display:flex;align-items: baseline"}).appendTo(row2); | ||||
|                     $('<div/>',{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 $('<input/>',{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 = $('<div/>', {style:"margin-top: 4px;"}).appendTo(row2); | ||||
|  | ||||
|                     var row3_1 = $('<div/>', {style:"display:flex;"}).appendTo(row3); | ||||
|                     var row3_1 = $('<div/>', {style:"display:flex;align-items: baseline"}).appendTo(row3); | ||||
|                     $('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"}) | ||||
|                         .text(search) | ||||
|                         .appendTo(row3_1); | ||||
|  | ||||
|                     function createFromValue() { | ||||
|                         return $('<input/>',{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 = $('<div/>',{style:"display:flex;margin-top:8px;"}).appendTo(row3); | ||||
|                     var row3_2 = $('<div/>',{style:"display:flex;margin-top:8px;align-items: baseline"}).appendTo(row3); | ||||
|                     $('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"}) | ||||
|                         .text(replace) | ||||
|                         .appendTo(row3_2); | ||||
|  | ||||
|                     function createToValue() { | ||||
|                         return $('<input/>',{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']}); | ||||
|                     } | ||||
|  | ||||
|                     $('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"}) | ||||
|                         .text(to) | ||||
|                         .appendTo(row4); | ||||
|  | ||||
|                     function createMoveValue() { | ||||
|                         return $('<input/>',{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'); | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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" | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user