From d47ef7e264b5fcef5951d39d48ffcf00c163631d Mon Sep 17 00:00:00 2001 From: ido Date: Mon, 9 Jan 2017 17:05:13 +0200 Subject: [PATCH] PR #131 --- src/options.js | 5 +++++ src/reactTemplates.js | 32 ++++++++++++++++++--------- test/data/autobind.rt | 2 ++ test/data/autobind.rt.js | 10 +++++++++ test/data/invalid/invalid-autobind.rt | 4 ++++ test/src/rt.invalid.spec.js | 3 ++- test/src/rt.valid.spec.js | 8 +++++++ 7 files changed, 52 insertions(+), 12 deletions(-) create mode 100644 test/data/autobind.rt create mode 100644 test/data/autobind.rt.js create mode 100644 test/data/invalid/invalid-autobind.rt diff --git a/src/options.js b/src/options.js index f70c584e..4c03624f 100644 --- a/src/options.js +++ b/src/options.js @@ -121,5 +121,10 @@ $ rt [ ...] []`, type: 'Boolean', default: 'false', description: 'Remove repeating whitespace from HTML text.' + }, { + option: 'autobind', + type: 'Boolean', + default: 'false', + description: 'Automatically bind event handlers to components' }] }); diff --git a/src/reactTemplates.js b/src/reactTemplates.js index 27c6c3c8..06bff01a 100644 --- a/src/reactTemplates.js +++ b/src/reactTemplates.js @@ -211,18 +211,28 @@ function generateProps(node, context) { } function handleEventHandler(val, context, node, key) { - const funcParts = val.split('=>'); - if (funcParts.length !== 2) { - throw RTCodeError.build(context, node, `when using 'on' events, use lambda '(p1,p2)=>body' notation or use {} to return a callback function. error: [${key}='${val}']`); - } - const evtParams = funcParts[0].replace('(', '').replace(')', '').trim(); - const funcBody = funcParts[1].trim(); - let params = context.boundParams; - if (evtParams.trim() !== '') { - params = params.concat([evtParams.trim()]); + let handlerString; + if (_.startsWith(val, 'this.')) { + if (context.options.autobind) { + handlerString = `${val}.bind(this)`; + } else { + throw RTCodeError.build(context, node, "'this.handler' syntax allowed only when the --autobind is on, use {} to return a callback function."); + } + } else { + const funcParts = val.split('=>'); + if (funcParts.length !== 2) { + throw RTCodeError.build(context, node, `when using 'on' events, use lambda '(p1,p2)=>body' notation or 'this.handler'; otherwise use {} to return a callback function. error: [${key}='${val}']`); + } + const evtParams = funcParts[0].replace('(', '').replace(')', '').trim(); + const funcBody = funcParts[1].trim(); + let params = context.boundParams; + if (evtParams.trim() !== '') { + params = params.concat([evtParams.trim()]); + } + const generatedFuncName = generateInjectedFunc(context, key, funcBody, params); + handlerString = genBind(generatedFuncName, context.boundParams); } - const generatedFuncName = generateInjectedFunc(context, key, funcBody, params); - return genBind(generatedFuncName, context.boundParams); + return handlerString; } function genBind(func, args) { diff --git a/test/data/autobind.rt b/test/data/autobind.rt new file mode 100644 index 00000000..b740b7d6 --- /dev/null +++ b/test/data/autobind.rt @@ -0,0 +1,2 @@ +
+ diff --git a/test/data/autobind.rt.js b/test/data/autobind.rt.js new file mode 100644 index 00000000..1d9eb061 --- /dev/null +++ b/test/data/autobind.rt.js @@ -0,0 +1,10 @@ +define([ + 'react', + 'lodash' +], function (React, _) { + 'use strict'; + return function () { + return React.createElement('div', { 'onKeyDown': this.handleKeyDown.bind(this) }); + }; +}); + diff --git a/test/data/invalid/invalid-autobind.rt b/test/data/invalid/invalid-autobind.rt new file mode 100644 index 00000000..46a366c1 --- /dev/null +++ b/test/data/invalid/invalid-autobind.rt @@ -0,0 +1,4 @@ +
+ This should trigger an error because evaluated with no autobinding (--autobind=false) +
+ diff --git a/test/src/rt.invalid.spec.js b/test/src/rt.invalid.spec.js index 54efbff4..08e75469 100644 --- a/test/src/rt.invalid.spec.js +++ b/test/src/rt.invalid.spec.js @@ -19,7 +19,8 @@ module.exports = { {file: 'invalid-scope.rt', issue: new RTCodeError("invalid scope part 'a in a in a'", 0, 35, 1, 1)}, {file: 'invalid-html.rt', issue: new RTCodeError('Document should have a root element', -1, -1, -1, -1)}, {file: 'invalid-exp.rt', issue: new RTCodeError("Failed to parse text '\n {z\n'", 5, 13, 1, 6)}, - {file: 'invalid-lambda.rt', issue: new RTCodeError("when using 'on' events, use lambda '(p1,p2)=>body' notation or use {} to return a callback function. error: [onClick='']", 0, 23, 1, 1)}, + {file: 'invalid-lambda.rt', issue: new RTCodeError("when using 'on' events, use lambda '(p1,p2)=>body' notation or 'this.handler'; otherwise use {} to return a callback function. error: [onClick='']", 0, 23, 1, 1)}, + {file: 'invalid-autobind.rt', issue: new RTCodeError("'this.handler' syntax allowed only when the --autobind is on, use {} to return a callback function.", 0, 132, 1, 1)}, // {file: 'invalid-js.rt', issue: new RTCodeError('Unexpected token ILLEGAL', 0, 32, 1, 1)}, bug interduced due to scope parsing {file: 'invalid-single-root.rt', issue: new RTCodeError('Document should have no more than a single root element', 12, 23, 2, 1)}, {file: 'invalid-repeat-1.rt', issue: new RTCodeError("rt-repeat invalid 'in' expression 'a in b in c'", 9, 44, 2, 4)}, diff --git a/test/src/rt.valid.spec.js b/test/src/rt.valid.spec.js index 5eb01f7c..8335962f 100644 --- a/test/src/rt.valid.spec.js +++ b/test/src/rt.valid.spec.js @@ -40,6 +40,14 @@ module.exports = { testFiles(t, files, options); }); + test('autobinding conversion test', t => { + const options = { + autobind: true + }; + const files = ['autobind.rt']; + testFiles(t, files, options); + }); + test('prop template conversion test', t => { const options = { propTemplates: {