Skip to content

Commit 0c01bcc

Browse files
committed
fix vuejs#512 attribute interpolation with filters
1 parent ed72137 commit 0c01bcc

File tree

3 files changed

+62
-9
lines changed

3 files changed

+62
-9
lines changed

src/parse/text.js

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
var Cache = require('../cache')
22
var config = require('../config')
3+
var dirParser = require('./directive')
34
var regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g
45
var cache, tagRE, htmlRE, firstChar, lastChar
56

@@ -106,6 +107,8 @@ exports.parse = function (text) {
106107

107108
/**
108109
* Format a list of tokens into an expression.
110+
* e.g. tokens parsed from 'a {{b}} c' can be serialized
111+
* into one single expression as '"a " + b + " c"'.
109112
*
110113
* @param {Array} tokens
111114
* @param {Vue} [vm]
@@ -115,23 +118,61 @@ exports.parse = function (text) {
115118
exports.tokensToExp = function (tokens, vm) {
116119
return tokens.length > 1
117120
? tokens.map(function (token) {
118-
return formatToken(token, vm)
119-
}).join('+')
120-
: formatToken(tokens[0], vm)
121+
return formatToken(token, vm)
122+
}).join('+')
123+
: formatToken(tokens[0], vm, true)
121124
}
122125

123126
/**
124127
* Format a single token.
125128
*
126129
* @param {Object} token
127130
* @param {Vue} [vm]
131+
* @param {Boolean} single
128132
* @return {String}
129133
*/
130134

131-
function formatToken (token, vm) {
135+
function formatToken (token, vm, single) {
132136
return token.tag
133137
? vm && token.oneTime
134-
? '"' + vm.$get(token.value) + '"'
135-
: '(' + token.value + ')'
138+
? '"' + vm.$eval(token.value) + '"'
139+
: single
140+
? token.value
141+
: inlineFilters(token.value)
136142
: '"' + token.value + '"'
143+
}
144+
145+
/**
146+
* For an attribute with multiple interpolation tags,
147+
* e.g. attr="some-{{thing | filter}}", in order to combine
148+
* the whole thing into a single watchable expression, we
149+
* have to inline those filters. This function does exactly
150+
* that. This is a bit hacky but it avoids heavy changes
151+
* to directive parser and watcher mechanism.
152+
*
153+
* @param {String} exp
154+
* @return {String}
155+
*/
156+
157+
var filterRE = /[^|]\|[^|]/
158+
function inlineFilters (exp) {
159+
if (!filterRE.test(exp)) {
160+
return '(' + exp + ')'
161+
} else {
162+
var dir = dirParser.parse(exp)[0]
163+
if (!dir.filters) {
164+
return '(' + exp + ')'
165+
} else {
166+
exp = dir.expression
167+
for (var i = 0, l = dir.filters.length; i < l; i++) {
168+
var filter = dir.filters[i]
169+
var args = filter.args
170+
? ',"' + filter.args.join('","') + '"'
171+
: ''
172+
exp = 'this.$options.filters["' + filter.name + '"]' +
173+
'.apply(this,[' + exp + args + '])'
174+
}
175+
return exp
176+
}
177+
}
137178
}

test/unit/specs/compile/compile_spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ if (_.inBrowser) {
138138
data['{{*b}}'] = 'B'
139139
el.innerHTML = '<div a="{{a}}" b="{{*b}}"></div>'
140140
var def = Vue.options.directives.attr
141-
var descriptor = dirParser.parse('a:(a)')[0]
141+
var descriptor = dirParser.parse('a:a')[0]
142142
var linker = compile(el, Vue.options)
143143
linker(vm, el)
144144
expect(vm._bindDir.calls.count()).toBe(1)

test/unit/specs/parse/text_spec.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,9 @@ describe('Text Parser', function () {
9696
})
9797

9898
it('tokens to expression', function () {
99-
var tokens = textParser.parse('view-{{test + 1}}-test-{{ok}}')
99+
var tokens = textParser.parse('view-{{test + 1}}-test-{{ok + "|"}}')
100100
var exp = textParser.tokensToExp(tokens)
101-
expect(exp).toBe('"view-"+(test + 1)+"-test-"+(ok)')
101+
expect(exp).toBe('"view-"+(test + 1)+"-test-"+(ok + "|")')
102102
})
103103

104104
it('tokens to expression with oneTime tags & vm', function () {
@@ -110,4 +110,16 @@ describe('Text Parser', function () {
110110
expect(exp).toBe('"view-"+"a"+"-test-"+(ok)')
111111
})
112112

113+
it('tokens to expression with filters, single expression', function () {
114+
var tokens = textParser.parse('{{test | abc}}')
115+
var exp = textParser.tokensToExp(tokens)
116+
expect(exp).toBe('test | abc')
117+
})
118+
119+
it('tokens to expression with filters, multiple expressions', function () {
120+
var tokens = textParser.parse('a {{b | c d}} e')
121+
var exp = textParser.tokensToExp(tokens)
122+
expect(exp).toBe('"a "+this.$options.filters["c"].apply(this,[b,"d"])+" e"')
123+
})
124+
113125
})

0 commit comments

Comments
 (0)