diff --git a/nodes/core/core/80-template.html b/nodes/core/core/80-template.html index 446284dbf..0bbf4da42 100644 --- a/nodes/core/core/80-template.html +++ b/nodes/core/core/80-template.html @@ -67,6 +67,7 @@ }

The resulting property will be:

Hello Fred. Today is Monday
+

It is possible to use property from flow context or global context. Just use {{flow.name}} or {{global.name}}.

By default, mustache will escape any HTML entities in the values it substitutes. To prevent this, use {{{triple}}} braces. diff --git a/nodes/core/core/80-template.js b/nodes/core/core/80-template.js index 8e93b1530..ba571d458 100644 --- a/nodes/core/core/80-template.js +++ b/nodes/core/core/80-template.js @@ -18,6 +18,39 @@ module.exports = function(RED) { "use strict"; var mustache = require("mustache"); + /** + * Custom Mustache Context capable to resolve message property and node + * flow and global context + */ + function NodeContext(msg, nodeContext) { + this.msgContext = new mustache.Context(msg); + this.nodeContext = nodeContext; + } + + NodeContext.prototype = new mustache.Context(); + + NodeContext.prototype.lookup = function (name) { + // try message first: + var value = this.msgContext.lookup(name); + if (value !== undefined) { + return value; + } + + // try node context: + var dot = name.indexOf("."); + if (dot > 0) { + var contextName = name.substr(0, dot); + var variableName = name.substr(dot + 1); + + if (contextName === "flow" && this.nodeContext.flow) { + return this.nodeContext.flow.get(variableName); + } + else if (contextName === "global" && this.nodeContext.global) { + return this.nodeContext.global.get(variableName); + } + } + } + function TemplateNode(n) { RED.nodes.createNode(this,n); this.name = n.name; @@ -31,7 +64,7 @@ module.exports = function(RED) { try { var value; if (node.syntax === "mustache") { - value = mustache.render(node.template,msg); + value = mustache.render(node.template, new NodeContext(msg, node.context())); } else { value = node.template; } diff --git a/test/nodes/core/core/80-template_spec.js b/test/nodes/core/core/80-template_spec.js index b93601989..686f77ede 100644 --- a/test/nodes/core/core/80-template_spec.js +++ b/test/nodes/core/core/80-template_spec.js @@ -43,6 +43,52 @@ describe('template node', function() { }); }); + it('should modify payload from flow context', function(done) { + var flow = [{id:"n1",z:"t1", type:"template", field:"payload", template:"payload={{flow.value}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; + helper.load(templateNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n1.context().flow.set("value","foo"); + n2.on("input", function(msg) { + msg.should.have.property('topic', 'bar'); + msg.should.have.property('payload', 'payload=foo'); + done(); + }); + n1.receive({payload:"foo",topic: "bar"}); + }); + }); + + it('should modify payload from global context', function(done) { + var flow = [{id:"n1",z:"t1", type:"template", field:"payload", template:"payload={{global.value}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; + helper.load(templateNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n1.context().global.set("value","foo"); + n2.on("input", function(msg) { + msg.should.have.property('topic', 'bar'); + msg.should.have.property('payload', 'payload=foo'); + done(); + }); + n1.receive({payload:"foo",topic: "bar"}); + }); + }); + + it('should handle missing node context', function(done) { + // this is artificial test because in flow there is missing z property (probably never happen in real usage) + var flow = [{id:"n1",type:"template", field:"payload", template:"payload={{flow.value}},{{global.value}}",wires:[["n2"]]},{id:"n2",type:"helper"}]; + helper.load(templateNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + msg.should.have.property('topic', 'bar'); + msg.should.have.property('payload', 'payload=,'); + done(); + }); + n1.receive({payload:"foo",topic: "bar"}); + }); + }); + + it('should modify payload in plain text mode', function(done) { var flow = [{id:"n1", type:"template", field:"payload", syntax:"plain", template:"payload={{payload}}",wires:[["n2"]]},{id:"n2",type:"helper"}]; helper.load(templateNode, flow, function() { @@ -57,32 +103,36 @@ describe('template node', function() { }); }); - xit('should modify flow context', function(done) { - var flow = [{id:"n1", type:"template", field:"payload", fieldType:"flow", template:"payload={{payload}}",wires:[["n2"]]},{id:"n2",type:"helper"}]; + it('should modify flow context', function(done) { + var flow = [{id:"n1",z:"t1", type:"template", field:"payload", fieldType:"flow", template:"payload={{payload}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; helper.load(templateNode, flow, function() { var n1 = helper.getNode("n1"); var n2 = helper.getNode("n2"); - setTimeout( function() { - console.log(n2); - console.log(n2.context().global.get("payload")); - //c.should.equal(1); // should only have had one output. + n2.on("input", function(msg) { + // mesage is intact + msg.should.have.property('topic', 'bar'); + msg.should.have.property('payload', 'foo'); + // result is in flow context + n2.context().flow.get("payload").should.equal("payload=foo"); done(); - },50); + }); n1.receive({payload:"foo",topic: "bar"}); }); }); - xit('should modify global context', function(done) { - var flow = [{id:"n1", type:"template", field:"payload", fieldType:"global", template:"payload={{payload}}",wires:[["n2"]]},{id:"n2",type:"helper"}]; + it('should modify global context', function(done) { + var flow = [{id:"n1",z:"t1", type:"template", field:"payload", fieldType:"global", template:"payload={{payload}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; helper.load(templateNode, flow, function() { var n1 = helper.getNode("n1"); var n2 = helper.getNode("n2"); - setTimeout( function() { - console.log(n2); - console.log(n2.context().global.get("payload")); - //c.should.equal(1); // should only have had one output. + n2.on("input", function(msg) { + // mesage is intact + msg.should.have.property('topic', 'bar'); + msg.should.have.property('payload', 'foo'); + // result is in global context + n2.context().global.get("payload").should.equal("payload=foo"); done(); - },50); + }); n1.receive({payload:"foo",topic: "bar"}); }); });