Skip to content

Commit 5b91727

Browse files
authored
feat: support bracket notation with quotes and dot notation (#41)
* feat: support bracket notation with quotes * feat: support dot notation
1 parent c0c085e commit 5b91727

File tree

2 files changed

+37
-7
lines changed

2 files changed

+37
-7
lines changed

domUtil/template.js

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ var isArray = require('../type/isArray');
1111
var isString = require('../type/isString');
1212
var extend = require('../object/extend');
1313

14-
var EXPRESSION_REGEXP = /{{\s?(\/?[a-zA-Z0-9_.@[\] ]+)\s?}}/g;
15-
var BRACKET_REGEXP = /^([a-zA-Z0-9_@]+)\[([a-zA-Z0-9_@]+)\]$/;
14+
var EXPRESSION_REGEXP = /{{\s?(\/?[a-zA-Z0-9_.@[\]"'\- ]+)\s?}}/g;
15+
var BRACKET_REGEXP = /^([a-zA-Z0-9_@]+)\[([a-zA-Z0-9_@"']+)\]$/;
16+
var DOT_REGEXP = /^([a-zA-Z_]+)\.([a-zA-Z_]+)$/;
17+
var STRING_REGEXP = /^["'](\w+)["']$/;
1618
var NUMBER_REGEXP = /^-?\d+\.?\d*$/;
1719

1820
var EXPRESSION_INTERVAL = 2;
@@ -30,17 +32,23 @@ var BLOCK_HELPERS = {
3032
* @returns {*}
3133
* @private
3234
*/
35+
// eslint-disable-next-line complexity
3336
function getValueFromContext(exp, context) {
34-
var bracketExps;
37+
var splitedExps;
3538
var value = context[exp];
3639

3740
if (exp === 'true') {
3841
value = true;
3942
} else if (exp === 'false') {
4043
value = false;
44+
} else if (STRING_REGEXP.test(exp)) {
45+
value = STRING_REGEXP.exec(exp)[1];
4146
} else if (BRACKET_REGEXP.test(exp)) {
42-
bracketExps = exp.split(BRACKET_REGEXP);
43-
value = getValueFromContext(bracketExps[1], context)[getValueFromContext(bracketExps[2], context)];
47+
splitedExps = exp.split(BRACKET_REGEXP);
48+
value = getValueFromContext(splitedExps[1], context)[getValueFromContext(splitedExps[2], context)];
49+
} else if (DOT_REGEXP.test(exp)) {
50+
splitedExps = exp.split(DOT_REGEXP);
51+
value = getValueFromContext(splitedExps[1], context)[splitedExps[2]];
4452
} else if (NUMBER_REGEXP.test(exp)) {
4553
value = parseFloat(exp);
4654
}
@@ -296,6 +304,10 @@ function compile(sources, context) {
296304
* <br>
297305
* If expression exists in the context, it will be replaced.
298306
* ex) '{{title}}' with context {title: 'Hello!'} is converted to 'Hello!'.
307+
* An array or object can be accessed using bracket and dot notation.
308+
* ex) '{{odds[2]}}' with context {odds: [1, 3, 5]} is converted to '5'.
309+
* ex) '{{evens[first]}}' with context {evens: [2, 4], first: 0} is converted to '2'.
310+
* ex) '{{project["name"]}}' and '{{project.name}}' with context {project: {name: 'CodeSnippet'}} is converted to 'CodeSnippet'.
299311
* <br>
300312
* If replaced expression is a function, next expressions will be arguments of the function.
301313
* ex) '{{add 1 2}}' with context {add: function(a, b) {return a + b;}} is converted to '3'.

test/template.spec.js

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,39 @@ describe('{{expression}}', function() {
2121
it('should bind with number if value is a number.', function() {
2222
expect(template('<p>{{ 3 }}</p>', {})).toBe('<p>3</p>');
2323
expect(template('<p>{{123.4567}}</p>', {})).toBe('<p>123.4567</p>');
24+
expect(template('<p>{{-1}}</p>', {})).toBe('<p>-1</p>');
25+
expect(template('<p>{{-0}}</p>', {})).toBe('<p>0</p>');
26+
expect(template('<p>{{-123.4567}}</p>', {})).toBe('<p>-123.4567</p>');
2427
});
2528

2629
it('should access the value with brackets if value is an object or array.', function() {
2730
expect(template('<p>{{ arr[2] }}</p>', {arr: [0, 1, 2]})).toBe('<p>2</p>');
28-
expect(template('<p>{{obj[key]}}</p>', {
31+
expect(template('<p>{{obj["key"]}}</p>', {obj: {key: 'value'}})).toBe('<p>value</p>');
32+
expect(template('<p>{{obj[name]}}</p>', {
2933
obj: {key: 'value'},
30-
key: 'key'
34+
name: 'key'
3135
})).toBe('<p>value</p>');
3236
expect(template('{{each nums}}{{nums[@index]}}{{/each}}', {nums: [1, 2, 3]})).toBe('123');
3337
});
3438

39+
it('should access the value with dots if value is an object.', function() {
40+
expect(template('<p>{{obj.key}}</p>', {obj: {key: 'value'}})).toBe('<p>value</p>');
41+
});
42+
3543
it('should bind with boolean if value is "true" or "false".', function() {
3644
expect(template('<p>{{ false }}</p>', {})).toBe('<p>false</p>');
3745
expect(template('<p>{{true}}</p>', {})).toBe('<p>true</p>');
3846
});
47+
48+
it('should bind with string if value is string with quotes.', function() {
49+
var context = {
50+
sayHello: function(name) {
51+
return 'Hello, ' + name;
52+
}
53+
};
54+
expect(template('<p>{{ sayHello "CodeSnippet" }}</p>', context)).toBe('<p>Hello, CodeSnippet</p>');
55+
expect(template('<p>{{sayHello \'world\'}}</p>', context)).toBe('<p>Hello, world</p>');
56+
});
3957
});
4058

4159
describe('{{helper arg1 arg2}}', function() {

0 commit comments

Comments
 (0)