diff --git a/domUtil/template.js b/domUtil/template.js
index c2408ef6..16038fe4 100644
--- a/domUtil/template.js
+++ b/domUtil/template.js
@@ -11,8 +11,10 @@ var isArray = require('../type/isArray');
var isString = require('../type/isString');
var extend = require('../object/extend');
-var EXPRESSION_REGEXP = /{{\s?(\/?[a-zA-Z0-9_.@[\] ]+)\s?}}/g;
-var BRACKET_REGEXP = /^([a-zA-Z0-9_@]+)\[([a-zA-Z0-9_@]+)\]$/;
+var EXPRESSION_REGEXP = /{{\s?(\/?[a-zA-Z0-9_.@[\]"'\- ]+)\s?}}/g;
+var BRACKET_REGEXP = /^([a-zA-Z0-9_@]+)\[([a-zA-Z0-9_@"']+)\]$/;
+var DOT_REGEXP = /^([a-zA-Z_]+)\.([a-zA-Z_]+)$/;
+var STRING_REGEXP = /^["'](\w+)["']$/;
var NUMBER_REGEXP = /^-?\d+\.?\d*$/;
var EXPRESSION_INTERVAL = 2;
@@ -30,17 +32,23 @@ var BLOCK_HELPERS = {
* @returns {*}
* @private
*/
+// eslint-disable-next-line complexity
function getValueFromContext(exp, context) {
- var bracketExps;
+ var splitedExps;
var value = context[exp];
if (exp === 'true') {
value = true;
} else if (exp === 'false') {
value = false;
+ } else if (STRING_REGEXP.test(exp)) {
+ value = STRING_REGEXP.exec(exp)[1];
} else if (BRACKET_REGEXP.test(exp)) {
- bracketExps = exp.split(BRACKET_REGEXP);
- value = getValueFromContext(bracketExps[1], context)[getValueFromContext(bracketExps[2], context)];
+ splitedExps = exp.split(BRACKET_REGEXP);
+ value = getValueFromContext(splitedExps[1], context)[getValueFromContext(splitedExps[2], context)];
+ } else if (DOT_REGEXP.test(exp)) {
+ splitedExps = exp.split(DOT_REGEXP);
+ value = getValueFromContext(splitedExps[1], context)[splitedExps[2]];
} else if (NUMBER_REGEXP.test(exp)) {
value = parseFloat(exp);
}
@@ -296,6 +304,10 @@ function compile(sources, context) {
*
* If expression exists in the context, it will be replaced.
* ex) '{{title}}' with context {title: 'Hello!'} is converted to 'Hello!'.
+ * An array or object can be accessed using bracket and dot notation.
+ * ex) '{{odds[2]}}' with context {odds: [1, 3, 5]} is converted to '5'.
+ * ex) '{{evens[first]}}' with context {evens: [2, 4], first: 0} is converted to '2'.
+ * ex) '{{project["name"]}}' and '{{project.name}}' with context {project: {name: 'CodeSnippet'}} is converted to 'CodeSnippet'.
*
* If replaced expression is a function, next expressions will be arguments of the function.
* ex) '{{add 1 2}}' with context {add: function(a, b) {return a + b;}} is converted to '3'.
diff --git a/test/template.spec.js b/test/template.spec.js
index a22f24f2..f144bae1 100644
--- a/test/template.spec.js
+++ b/test/template.spec.js
@@ -21,21 +21,39 @@ describe('{{expression}}', function() {
it('should bind with number if value is a number.', function() {
expect(template('
{{ 3 }}
', {})).toBe('3
'); expect(template('{{123.4567}}
', {})).toBe('123.4567
'); + expect(template('{{-1}}
', {})).toBe('-1
'); + expect(template('{{-0}}
', {})).toBe('0
'); + expect(template('{{-123.4567}}
', {})).toBe('-123.4567
'); }); it('should access the value with brackets if value is an object or array.', function() { expect(template('{{ arr[2] }}
', {arr: [0, 1, 2]})).toBe('2
'); - expect(template('{{obj[key]}}
', { + expect(template('{{obj["key"]}}
', {obj: {key: 'value'}})).toBe('value
'); + expect(template('{{obj[name]}}
', { obj: {key: 'value'}, - key: 'key' + name: 'key' })).toBe('value
'); expect(template('{{each nums}}{{nums[@index]}}{{/each}}', {nums: [1, 2, 3]})).toBe('123'); }); + it('should access the value with dots if value is an object.', function() { + expect(template('{{obj.key}}
', {obj: {key: 'value'}})).toBe('value
'); + }); + it('should bind with boolean if value is "true" or "false".', function() { expect(template('{{ false }}
', {})).toBe('false
'); expect(template('{{true}}
', {})).toBe('true
'); }); + + it('should bind with string if value is string with quotes.', function() { + var context = { + sayHello: function(name) { + return 'Hello, ' + name; + } + }; + expect(template('{{ sayHello "CodeSnippet" }}
', context)).toBe('Hello, CodeSnippet
'); + expect(template('{{sayHello \'world\'}}
', context)).toBe('Hello, world
'); + }); }); describe('{{helper arg1 arg2}}', function() {