Skip to content

Commit a2e287f

Browse files
author
Evan You
committed
add support for interpolation in html attributes
1 parent 354f559 commit a2e287f

File tree

9 files changed

+161
-86
lines changed

9 files changed

+161
-86
lines changed

src/compiler.js

+34-19
Original file line numberDiff line numberDiff line change
@@ -242,15 +242,15 @@ CompilerProto.compile = function (node, root) {
242242
if (repeatExp = utils.attr(node, 'repeat')) {
243243

244244
// repeat block cannot have v-id at the same time.
245-
directive = Directive.parse(config.attrs.repeat, repeatExp, compiler, node)
245+
directive = Directive.parse('repeat', repeatExp, compiler, node)
246246
if (directive) {
247247
compiler.bindDirective(directive)
248248
}
249249

250250
// v-component has 2nd highest priority
251251
} else if (!root && (componentExp = utils.attr(node, 'component'))) {
252252

253-
directive = Directive.parse(config.attrs.component, componentExp, compiler, node)
253+
directive = Directive.parse('component', componentExp, compiler, node)
254254
if (directive) {
255255
// component directive is a bit different from the others.
256256
// when it has no argument, it should be treated as a
@@ -293,28 +293,44 @@ CompilerProto.compile = function (node, root) {
293293
* Compile a normal node
294294
*/
295295
CompilerProto.compileNode = function (node) {
296-
var i, j, attrs = node.attributes
296+
var i, j,
297+
attrs = node.attributes,
298+
prefix = config.prefix + '-'
297299
// parse if has attributes
298300
if (attrs && attrs.length) {
299-
var attr, valid, exps, exp
301+
var attr, isDirective, exps, exp, directive
300302
// loop through all attributes
301303
i = attrs.length
302304
while (i--) {
303305
attr = attrs[i]
304-
valid = false
305-
exps = Directive.split(attr.value)
306-
// loop through clauses (separated by ",")
307-
// inside each attribute
308-
j = exps.length
309-
while (j--) {
310-
exp = exps[j]
311-
var directive = Directive.parse(attr.name, exp, this, node)
312-
if (directive) {
313-
valid = true
314-
this.bindDirective(directive)
306+
isDirective = false
307+
308+
if (attr.name.indexOf(prefix) === 0) {
309+
// a directive - split, parse and bind it.
310+
isDirective = true
311+
exps = Directive.split(attr.value)
312+
// loop through clauses (separated by ",")
313+
// inside each attribute
314+
j = exps.length
315+
while (j--) {
316+
exp = exps[j]
317+
directive = Directive.parse(attr.name.slice(prefix.length), exp, this, node)
318+
if (directive) {
319+
this.bindDirective(directive)
320+
}
321+
}
322+
} else {
323+
// non directive attribute, check interpolation tags
324+
exp = TextParser.parseAttr(attr.value)
325+
if (exp) {
326+
directive = Directive.parse('attr', attr.name + ':' + exp, this, node)
327+
if (directive) {
328+
this.bindDirective(directive)
329+
}
315330
}
316331
}
317-
if (valid) node.removeAttribute(attr.name)
332+
333+
if (isDirective) node.removeAttribute(attr.name)
318334
}
319335
}
320336
// recursively compile childNodes
@@ -332,8 +348,7 @@ CompilerProto.compileNode = function (node) {
332348
CompilerProto.compileTextNode = function (node) {
333349
var tokens = TextParser.parse(node.nodeValue)
334350
if (!tokens) return
335-
var dirname = config.attrs.text,
336-
el, token, directive
351+
var el, token, directive
337352
for (var i = 0, l = tokens.length; i < l; i++) {
338353
token = tokens[i]
339354
if (token.key) { // a binding
@@ -346,7 +361,7 @@ CompilerProto.compileTextNode = function (node) {
346361
}
347362
} else { // a binding
348363
el = document.createTextNode('')
349-
directive = Directive.parse(dirname, token.key, this, el)
364+
directive = Directive.parse('text', token.key, this, el)
350365
if (directive) {
351366
this.bindDirective(directive)
352367
}

src/directive.js

+1-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
var config = require('./config'),
2-
utils = require('./utils'),
1+
var utils = require('./utils'),
32
directives = require('./directives'),
43
filters = require('./filters'),
54

@@ -201,10 +200,6 @@ Directive.split = function (exp) {
201200
*/
202201
Directive.parse = function (dirname, expression, compiler, node) {
203202

204-
var prefix = config.prefix + '-'
205-
if (dirname.indexOf(prefix) !== 0) return
206-
dirname = dirname.slice(prefix.length)
207-
208203
var dir = compiler.getOption('directives', dirname) || directives[dirname]
209204
if (!dir) return utils.warn('unknown directive: ' + dirname)
210205

src/text-parser.js

+32-18
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,36 @@
11
var BINDING_RE = /\{\{(.+?)\}\}/
22

3-
module.exports = {
3+
/**
4+
* Parse a piece of text, return an array of tokens
5+
*/
6+
function parse (text) {
7+
if (!BINDING_RE.test(text)) return null
8+
var m, i, tokens = []
9+
/* jshint boss: true */
10+
while (m = text.match(BINDING_RE)) {
11+
i = m.index
12+
if (i > 0) tokens.push(text.slice(0, i))
13+
tokens.push({ key: m[1].trim() })
14+
text = text.slice(i + m[0].length)
15+
}
16+
if (text.length) tokens.push(text)
17+
return tokens
18+
}
419

5-
/**
6-
* Parse a piece of text, return an array of tokens
7-
*/
8-
parse: function (text) {
9-
if (!BINDING_RE.test(text)) return null
10-
var m, i, tokens = []
11-
/* jshint boss: true */
12-
while (m = text.match(BINDING_RE)) {
13-
i = m.index
14-
if (i > 0) tokens.push(text.slice(0, i))
15-
tokens.push({ key: m[1].trim() })
16-
text = text.slice(i + m[0].length)
17-
}
18-
if (text.length) tokens.push(text)
19-
return tokens
20+
/**
21+
* Parse an attribute value with possible interpolation tags
22+
* return a Directive-friendly expression
23+
*/
24+
function parseAttr (attr) {
25+
var tokens = parse(attr)
26+
if (!tokens) return null
27+
var res = [], token
28+
for (var i = 0, l = tokens.length; i < l; i++) {
29+
token = tokens[i]
30+
res.push(token.key || ('"' + token + '"'))
2031
}
21-
22-
}
32+
return res.join('+')
33+
}
34+
35+
exports.parse = parse
36+
exports.parseAttr = parseAttr

test/functional/fixtures/expression.html

+10-1
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@
2323
<button v-on="click: ok = !ok" class="toggle">toggle</button>
2424
<button v-on="click: noMsg = 'Nah'" class="change">change</button>
2525
</div>
26+
<div id="attrs" data-test="hi {{msg}} ha"></div>
2627
<script>
27-
Vue.config({debug:true})
28+
Vue.config({debug:true})
29+
2830
var normal = new Vue({
2931
el: '#normal',
3032
data: {
@@ -54,6 +56,13 @@
5456
noMsg: 'NO'
5557
}
5658
})
59+
60+
var attrs = new Vue({
61+
el: '#attrs',
62+
data: {
63+
msg: 'ho'
64+
}
65+
})
5766
</script>
5867
</body>
5968
</html>

test/functional/specs/expression.js

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
/* global normal */
1+
/* global normal, attrs */
22

3-
casper.test.begin('Expression', 19, function (test) {
3+
casper.test.begin('Expression', 21, function (test) {
44

55
casper
66
.start('./fixtures/expression.html')
@@ -11,6 +11,10 @@ casper.test.begin('Expression', 19, function (test) {
1111
test.assertField('two', 'World')
1212
test.assertField('three', 'Hi')
1313
test.assertField('four', 'Ho')
14+
// attrs
15+
test.assertEval(function () {
16+
return document.getElementById('attrs').dataset.test === 'hi ho ha'
17+
})
1418
})
1519
.thenEvaluate(function () {
1620
// setting value
@@ -67,6 +71,14 @@ casper.test.begin('Expression', 19, function (test) {
6771
.thenClick('#conditional .change', function () {
6872
test.assertSelectorHasText('#conditional p', 'Nah')
6973
})
74+
.thenEvaluate(function () {
75+
attrs.msg = 'hoho'
76+
})
77+
.then(function () {
78+
test.assertEval(function () {
79+
return document.getElementById('attrs').dataset.test === 'hi hoho ha'
80+
})
81+
})
7082
.run(function () {
7183
test.done()
7284
})

0 commit comments

Comments
 (0)