Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#214, rt-template with in-place child component #216

Open
wants to merge 1 commit into
base: gh-pages
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,35 @@ export default function () {
};
```

### defining in-place child properties
In cases you'd like to set a renderable React component itself (not a function) as a child property,
You should use a **rt-template** tag with **in-place** attribute: `<rt-template prop="propName" in-place/>`.
Note that **argument** attribute and **in-place** attribute cannot be set simulatenously.

###### Sample:
```html
<div>
<rt-template prop="templateProp" in-place>
<div>some</div>
</rt-template>
</div>
```
###### Compiled (AMD):
```javascript
define([
'react',
'lodash'
], function (React, _) {
'use strict';
return function () {
function templateProp1() {
return React.createElement('div', {}, 'some');
}
return React.createElement('div', { 'templateProp': templateProp1.call(this) });
};
});
```

## Contributing

See the [Contributing page](CONTRIBUTING.md).
Expand Down
24 changes: 18 additions & 6 deletions src/reactTemplates.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ const classAttr = 'class';
const scopeAttr = 'rt-scope';
const propsAttr = 'rt-props';
const templateNode = 'rt-template';
const templateNode_PropAttr = 'prop';
const templateNode_ArgumentsAttr = 'arguments';
const templateNode_InPlaceAttr = 'in-place';
const virtualNode = 'rt-virtual';
const includeNode = 'rt-include';
const includeSrcAttr = 'src';
Expand Down Expand Up @@ -123,17 +126,21 @@ function generateTemplateProps(node, context) {
.map((child, index) => {
let templateProp = null;
if (child.name === templateNode) { // Generic explicit template tag
if (!_.has(child.attribs, 'prop')) {
throw RTCodeError.build(context, child, 'rt-template must have a prop attribute');
if (!_.has(child.attribs, templateNode_PropAttr)) {
throw RTCodeError.build(context, child, "'rt-template' should have a " + templateNode_PropAttr + ' attribute');
}
if (_.filter(child.children, {type: 'tag'}).length !== 1) {
throw RTCodeError.build(context, child, "'rt-template' should have a single non-text element as direct child");
}
if (_.has(child.attribs, templateNode_ArgumentsAttr) && _.has(child.attribs, templateNode_InPlaceAttr)) {
throw RTCodeError.build(context, child, "'" + templateNode_ArgumentsAttr + "' and '" + templateNode_InPlaceAttr + "' cannot coexist together");
}

const childTemplate = _.find(context.options.propTemplates, {prop: child.attribs.prop}) || {arguments: []};
const childTemplate = _.find(context.options.propTemplates, {prop: child.attribs[templateNode_PropAttr]}) || {arguments: []};
templateProp = {
prop: child.attribs.prop,
arguments: (child.attribs.arguments ? child.attribs.arguments.split(',') : childTemplate.arguments) || []
prop: child.attribs[templateNode_PropAttr],
arguments: (child.attribs[templateNode_ArgumentsAttr] ? child.attribs[templateNode_ArgumentsAttr].split(',') : childTemplate.arguments) || [],
inplace: _.has(child.attribs, templateNode_InPlaceAttr)
};
} else if (propTemplateDefinition && propTemplateDefinition[child.name]) { // Implicit child template from configuration
templateProp = {
Expand Down Expand Up @@ -161,7 +168,7 @@ function generateTemplateProps(node, context) {
context.boundParams = oldBoundParams;

const generatedFuncName = generateInjectedFunc(context, templateProp.prop, functionBody, functionParams);
props[templateProp.prop] = genBind(generatedFuncName, _.values(context.boundParams));
props[templateProp.prop] = templateProp.inplace ? genCall(generatedFuncName, _.values(context.boundParams)) : genBind(generatedFuncName, _.values(context.boundParams));

// Remove the template child from the children definition.
node.children.splice(templateProp.childIndex, 1);
Expand Down Expand Up @@ -240,6 +247,11 @@ function genBind(func, args) {
return `${func}.bind(${bindArgs.join(',')})`;
}

function genCall(func, args) {
const bindArgs = ['this'].concat(args);
return `${func}.call(${bindArgs.join(',')})`;
}

function handleStyleProp(val, node, context) {
const styleStr = _(val)
.split(';')
Expand Down
5 changes: 5 additions & 0 deletions test/data/invalid/invalid-rt-template-3.rt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<div>
<rt-template props="some">
<div>1</div>
</rt-template>
</div>
5 changes: 5 additions & 0 deletions test/data/invalid/invalid-rt-template-4.rt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<div>
<rt-template prop="some" arguments="" in-place>
<div>1</div>
</rt-template>
</div>
5 changes: 5 additions & 0 deletions test/data/propTemplates/inplaceTemplate.rt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<div>
<rt-template prop="templateProp" in-place>
<div>some</div>
</rt-template>
</div>
12 changes: 12 additions & 0 deletions test/data/propTemplates/inplaceTemplate.rt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
define([
'react',
'lodash'
], function (React, _) {
'use strict';
return function () {
function templateProp1() {
return React.createElement('div', {}, 'some');
}
return React.createElement('div', { 'templateProp': templateProp1.call(this) });
};
});
5 changes: 5 additions & 0 deletions test/data/propTemplates/inplaceTemplateInScope.rt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<div rt-scope="'boten' as name">
<rt-template prop="templateProp" in-place>
<div>Name: {name} some</div>
</rt-template>
</div>
16 changes: 16 additions & 0 deletions test/data/propTemplates/inplaceTemplateInScope.rt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
define([
'react',
'lodash'
], function (React, _) {
'use strict';
return function () {
function templateProp1(name) {
return React.createElement('div', {}, 'Name: ', name, ' some');
}
function scopeName2() {
var name = 'boten';
return React.createElement('div', { 'templateProp': templateProp1.call(this, name) });
}
return scopeName2.apply(this, []);
};
});
2 changes: 2 additions & 0 deletions test/src/rt.invalid.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ module.exports = {
{file: 'invalid-rt-import-4.rt', issue: new RTCodeError("'rt-import' must be a toplevel node", 9, 54, 2, 4)},
{file: 'invalid-rt-template-1.rt', issue: new RTCodeError("'rt-template' should have a single non-text element as direct child", 9, 88, 2, 4)},
{file: 'invalid-rt-template-2.rt', issue: new RTCodeError("'rt-template' should have a single non-text element as direct child", 9, 90, 2, 4)},
{file: 'invalid-rt-template-3.rt', issue: new RTCodeError("'rt-template' should have a prop attribute", 9, 72, 2, 4)},
{file: 'invalid-rt-template-4.rt', issue: new RTCodeError("'arguments' and 'in-place' cannot coexist together", 9, 93, 2, 4)},
{file: 'invalid-brace.rt', issue: new RTCodeError('Unexpected end of input', 128, 163, 5, 11)},
{file: 'invalid-style-1.rt', issue: new RTCodeError('Unexpected token ILLEGAL', 10, 39, 2, 5)},
{file: 'invalid-style-2.rt', issue: new RTCodeError('style attribute keys cannot contain { } expressions', 35, 68, 2, 5)},
Expand Down
5 changes: 4 additions & 1 deletion test/src/rt.valid.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ module.exports = {

test('autobinding conversion test', t => {
const options = {
modules: 'amd',
autobind: true
};
const files = ['autobind.rt'];
Expand All @@ -62,7 +63,9 @@ module.exports = {
'templateInScope.rt',
'implicitTemplate.rt',
'twoTemplates.rt',
'siblingTemplates.rt'
'siblingTemplates.rt',
'inplaceTemplate.rt',
'inplaceTemplateInScope.rt'
].map(file => path.join('propTemplates', file));
testFiles(t, files, options);
});
Expand Down