Skip to content

Commit 5dfa2f0

Browse files
committed
Filter syntax change
- quoted arguments are treated as plain strings - non-quoted arguments are treated as dynamic paths
1 parent 046dabd commit 5dfa2f0

File tree

11 files changed

+122
-92
lines changed

11 files changed

+122
-92
lines changed

examples/todomvc/index.html

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ <h1>todos</h1>
1616
autocomplete="off"
1717
placeholder="What needs to be done?"
1818
v-model="newTodo"
19-
v-on="keyup:addTodo | key enter"
19+
v-on="keyup:addTodo | key 'enter'"
2020
>
2121
</header>
2222
<section id="main" v-show="todos.length" v-cloak>
@@ -50,8 +50,8 @@ <h1>todos</h1>
5050
v-todo-focus="this == editedTodo"
5151
v-on="
5252
blur : doneEdit(this),
53-
keyup : doneEdit(this) | key enter,
54-
keyup : cancelEdit(this) | key esc
53+
keyup : doneEdit(this) | key 'enter',
54+
keyup : cancelEdit(this) | key 'esc'
5555
"
5656
>
5757
</li>

src/filters/array-filters.js

+11-24
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,17 @@ var Path = require('../parsers/path')
99
* @param {String} dataKey
1010
*/
1111

12-
exports.filterBy = function (arr, searchKey, delimiter, dataKey) {
12+
exports.filterBy = function (arr, search, delimiter, dataKey) {
1313
// allow optional `in` delimiter
1414
// because why not
1515
if (delimiter && delimiter !== 'in') {
1616
dataKey = delimiter
1717
}
18-
// get the search string
19-
var search =
20-
_.stripQuotes(searchKey) ||
21-
this.$get(searchKey)
2218
if (!search) {
2319
return arr
2420
}
21+
// cast to lowercase string
2522
search = ('' + search).toLowerCase()
26-
// get the optional dataKey
27-
dataKey =
28-
dataKey &&
29-
(_.stripQuotes(dataKey) || this.$get(dataKey))
3023
return arr.filter(function (item) {
3124
return dataKey
3225
? contains(Path.get(item, dataKey), search)
@@ -38,35 +31,29 @@ exports.filterBy = function (arr, searchKey, delimiter, dataKey) {
3831
* Filter filter for v-repeat
3932
*
4033
* @param {String} sortKey
41-
* @param {String} reverseKey
34+
* @param {String} reverse
4235
*/
4336

44-
exports.orderBy = function (arr, sortKey, reverseKey) {
45-
var key =
46-
_.stripQuotes(sortKey) ||
47-
this.$get(sortKey)
48-
if (!key) {
37+
exports.orderBy = function (arr, sortKey, reverse) {
38+
if (!sortKey) {
4939
return arr
5040
}
5141
var order = 1
52-
if (reverseKey) {
53-
if (reverseKey === '-1') {
42+
if (arguments.length > 2) {
43+
if (reverse === '-1') {
5444
order = -1
55-
} else if (reverseKey.charCodeAt(0) === 0x21) { // !
56-
reverseKey = reverseKey.slice(1)
57-
order = this.$get(reverseKey) ? 1 : -1
5845
} else {
59-
order = this.$get(reverseKey) ? -1 : 1
46+
order = reverse ? -1 : 1
6047
}
6148
}
6249
// sort on a copy to avoid mutating original array
6350
return arr.slice().sort(function (a, b) {
64-
if (key !== '$key' && key !== '$value') {
51+
if (sortKey !== '$key' && sortKey !== '$value') {
6552
if (a && '$value' in a) a = a.$value
6653
if (b && '$value' in b) b = b.$value
6754
}
68-
a = _.isObject(a) ? Path.get(a, key) : a
69-
b = _.isObject(b) ? Path.get(b, key) : b
55+
a = _.isObject(a) ? Path.get(a, sortKey) : a
56+
b = _.isObject(b) ? Path.get(b, sortKey) : b
7057
return a === b ? 0 : a > b ? order : -order
7158
})
7259
}

src/parsers/directive.js

+12-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ var Cache = require('../cache')
33
var cache = new Cache(1000)
44
var argRE = /^[^\{\?]+$|^'[^']*'$|^"[^"]*"$/
55
var filterTokenRE = /[^\s'"]+|'[^']+'|"[^"]+"/g
6+
var reservedArgRE = /^in$|^-?\d+/
67

78
/**
89
* Parser state
@@ -49,7 +50,17 @@ function pushFilter () {
4950
filter = {}
5051
var tokens = exp.match(filterTokenRE)
5152
filter.name = tokens[0]
52-
filter.args = tokens.length > 1 ? tokens.slice(1) : null
53+
if (tokens.length > 1) {
54+
filter.args = tokens.slice(1).map(function (arg) {
55+
var stripped = reservedArgRE.test(arg)
56+
? arg
57+
: _.stripQuotes(arg)
58+
return {
59+
value: stripped || arg,
60+
dynamic: !stripped
61+
}
62+
})
63+
}
5364
}
5465
if (filter) {
5566
(dir.filters = dir.filters || []).push(filter)

src/parsers/text.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ function inlineFilters (exp, single) {
166166
for (var i = 0, l = dir.filters.length; i < l; i++) {
167167
var filter = dir.filters[i]
168168
var args = filter.args
169-
? ',"' + filter.args.join('","') + '"'
169+
? ',' + JSON.stringify(filter.args).slice(1, -1)
170170
: ''
171171
exp = 'this._applyFilter("' + filter.name + '",[' + exp + args + '])'
172172
}

src/util/misc.js

+18-2
Original file line numberDiff line numberDiff line change
@@ -81,22 +81,38 @@ exports.resolveFilters = function (vm, filters, target) {
8181
if (!res.read) res.read = []
8282
res.read.push(function (value) {
8383
return args
84-
? reader.apply(vm, [value].concat(args))
84+
? reader.apply(vm, [value].concat(resolveArgs(vm, args)))
8585
: reader.call(vm, value)
8686
})
8787
}
8888
if (writer) {
8989
if (!res.write) res.write = []
9090
res.write.push(function (value, oldVal) {
9191
return args
92-
? writer.apply(vm, [value, oldVal].concat(args))
92+
? writer.apply(vm, [value, oldVal].concat(resolveArgs(vm, args)))
9393
: writer.call(vm, value, oldVal)
9494
})
9595
}
9696
})
9797
return res
9898
}
9999

100+
/**
101+
* Resolve arguments into static values
102+
*
103+
* @param {Vue} vm
104+
* @param {Array} args
105+
* @return {Array}
106+
*/
107+
108+
function resolveArgs (vm, args) {
109+
return args.map(function (arg) {
110+
return arg.dynamic
111+
? vm.$get(arg.value)
112+
: arg.value
113+
})
114+
}
115+
100116
/**
101117
* Apply filters to a value
102118
*

test/unit/specs/directives/on_spec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ if (_.inBrowser) {
5353
it('with key filter', function (done) {
5454
var vm = new Vue({
5555
el: el,
56-
template: '<a v-on="keyup:test | key enter">{{a}}</a>',
56+
template: '<a v-on="keyup:test | key \'enter\'">{{a}}</a>',
5757
data: {a:1},
5858
methods: {
5959
test: function () {

test/unit/specs/filters/filters_spec.js

+14-36
Original file line numberDiff line numberDiff line change
@@ -102,41 +102,28 @@ describe('Filters', function () {
102102
{ a: 2, b: 'hello'},
103103
{ a: 3, b: ['yoyo'] }
104104
]
105-
var vm = new Vue({
106-
data: {
107-
search: {
108-
key: 'hello',
109-
datakey: 'b.c',
110-
n: 2
111-
}
112-
}
113-
})
114105
var res
115106
// normal
116-
res = filter.call(vm, arr, 'search.key')
107+
res = filter(arr, 'hello')
117108
expect(res.length).toBe(2)
118109
expect(res[0]).toBe(arr[0])
119110
expect(res[1]).toBe(arr[1])
120111
// data key
121-
res = filter.call(vm, arr, 'search.key', 'search.datakey')
122-
expect(res.length).toBe(1)
123-
expect(res[0]).toBe(arr[0])
124-
// quotes
125-
res = filter.call(vm, arr, "'hello'", "'b.c'")
112+
res = filter(arr, 'hello', 'b.c')
126113
expect(res.length).toBe(1)
127114
expect(res[0]).toBe(arr[0])
128115
// delimiter
129-
res = filter.call(vm, arr, 'search.key', 'in', 'search.datakey')
116+
res = filter(arr, 'hello', 'in', 'b.c')
130117
expect(res.length).toBe(1)
131118
expect(res[0]).toBe(arr[0])
132119
// no search key
133-
res = filter.call(vm, arr, 'abc')
120+
res = filter(arr, null)
134121
expect(res).toBe(arr)
135122
// number search key
136-
res = filter.call(vm, arr, 'search.n')
123+
res = filter(arr, 2)
137124
expect(res[0]).toBe(arr[1])
138125
// search in sub array
139-
res = filter.call(vm, arr, "'yoyo'")
126+
res = filter(arr, 'yoyo')
140127
expect(res.length).toBe(1)
141128
expect(res[0]).toBe(arr[2])
142129
})
@@ -150,56 +137,47 @@ describe('Filters', function () {
150137
]
151138
var res
152139
// sort key
153-
res = filter.call(new Vue({
154-
data: {
155-
sortby: 'a.b',
156-
}
157-
}), arr, 'sortby')
140+
res = filter(arr, 'a.b')
158141
expect(res.length).toBe(3)
159142
expect(res[0].a.b).toBe(0)
160143
expect(res[1].a.b).toBe(1)
161144
expect(res[2].a.b).toBe(2)
162145
// reverse key
163-
res = filter.call(new Vue({
164-
data: { sortby: 'a.b', reverse: true }
165-
}), arr, 'sortby', 'reverse')
146+
res = filter(arr, 'a.b', true)
166147
expect(res.length).toBe(3)
167148
expect(res[0].a.b).toBe(2)
168149
expect(res[1].a.b).toBe(1)
169150
expect(res[2].a.b).toBe(0)
170151
// literal args
171-
res = filter.call(new Vue(), arr, "'c'", '-1')
152+
res = filter(arr, 'c', '-1')
172153
expect(res.length).toBe(3)
173154
expect(res[0].c).toBe('c')
174155
expect(res[1].c).toBe('b')
175156
expect(res[2].c).toBe('a')
176157
// negate reverse
177-
res = filter.call(new Vue({
178-
data: { reverse: true }
179-
}), arr, "'c'", '!reverse')
158+
res = filter(arr, 'c', false)
180159
expect(res.length).toBe(3)
181160
expect(res[0].c).toBe('a')
182161
expect(res[1].c).toBe('b')
183162
expect(res[2].c).toBe('c')
184163
// no sort key
185-
res = filter.call(new Vue(), arr, 'abc')
164+
res = filter(arr, null)
186165
expect(res).toBe(arr)
187166
})
188167

189168
it('orderBy on Object-converted array', function () {
190169
// object converted
191170
var filter = filters.orderBy
192-
var vm = new Vue()
193171
var arr = [
194172
{ $key: 'a', $value: 3 },
195173
{ $key: 'c', $value: 1 },
196174
{ $key: 'b', $value: 2 }
197175
]
198-
var res = filter.call(vm, arr, '"$key"')
176+
var res = filter(arr, '$key')
199177
expect(res[0].$value).toBe(3)
200178
expect(res[1].$value).toBe(2)
201179
expect(res[2].$value).toBe(1)
202-
res = filter.call(vm, arr, '"$value"')
180+
res = filter(arr, '$value')
203181
expect(res[0].$value).toBe(1)
204182
expect(res[1].$value).toBe(2)
205183
expect(res[2].$value).toBe(3)
@@ -209,7 +187,7 @@ describe('Filters', function () {
209187
{ $key: 'c', $value: { v: 1 } },
210188
{ $key: 'b', $value: { v: 2 } }
211189
]
212-
res = filter.call(vm, arr, '"v"')
190+
res = filter(arr, 'v')
213191
expect(res[0].$value.v).toBe(1)
214192
expect(res[1].$value.v).toBe(2)
215193
expect(res[2].$value.v).toBe(3)

test/unit/specs/parsers/directive_spec.js

+18-12
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,20 @@ describe('Directive Parser', function () {
1818
})
1919

2020
it('with filters', function () {
21-
var res = parse(' arg : exp | abc de | bcd')
21+
var res = parse(' arg : exp | abc de \'ok\' | bcd')
2222
expect(res.length).toBe(1)
2323
expect(res[0].expression).toBe('exp')
2424
expect(res[0].arg).toBe('arg')
25-
expect(res[0].raw).toBe('arg : exp | abc de | bcd')
25+
expect(res[0].raw).toBe('arg : exp | abc de \'ok\' | bcd')
2626
expect(res[0].filters.length).toBe(2)
2727
expect(res[0].filters[0].name).toBe('abc')
28-
expect(res[0].filters[0].args.length).toBe(1)
29-
expect(res[0].filters[0].args[0]).toBe('de')
28+
expect(res[0].filters[0].args.length).toBe(2)
29+
expect(res[0].filters[0].args[0].value).toBe('de')
30+
expect(res[0].filters[0].args[0].dynamic).toBe(true)
31+
expect(res[0].filters[0].args[1].value).toBe('ok')
32+
expect(res[0].filters[0].args[1].dynamic).toBe(false)
3033
expect(res[0].filters[1].name).toBe('bcd')
31-
expect(res[0].filters[1].args).toBeNull()
34+
expect(res[0].filters[1].args).toBeUndefined()
3235
})
3336

3437
it('double pipe', function () {
@@ -38,7 +41,7 @@ describe('Directive Parser', function () {
3841
expect(res[0].raw).toBe('a || b | c')
3942
expect(res[0].filters.length).toBe(1)
4043
expect(res[0].filters[0].name).toBe('c')
41-
expect(res[0].filters[0].args).toBeNull()
44+
expect(res[0].filters[0].args).toBeUndefined()
4245
})
4346

4447
it('single quote + boolean', function () {
@@ -65,31 +68,34 @@ describe('Directive Parser', function () {
6568
})
6669

6770
it('multiple complex clauses', function () {
68-
var res = parse('a:b | c | j, d:e | f | k l, g:h | i')
71+
var res = parse('a:b | c | j, d:e | f | k l, g:h | i "k"')
6972
expect(res.length).toBe(3)
7073

7174
expect(res[0].arg).toBe('a')
7275
expect(res[0].expression).toBe('b')
7376
expect(res[0].filters.length).toBe(2)
7477
expect(res[0].filters[0].name).toBe('c')
75-
expect(res[0].filters[0].args).toBeNull()
78+
expect(res[0].filters[0].args).toBeUndefined()
7679
expect(res[0].filters[1].name).toBe('j')
77-
expect(res[0].filters[1].args).toBeNull()
80+
expect(res[0].filters[1].args).toBeUndefined()
7881

7982
expect(res[1].arg).toBe('d')
8083
expect(res[1].expression).toBe('e')
8184
expect(res[1].filters.length).toBe(2)
8285
expect(res[1].filters[0].name).toBe('f')
83-
expect(res[1].filters[0].args).toBeNull()
86+
expect(res[1].filters[0].args).toBeUndefined()
8487
expect(res[1].filters[1].name).toBe('k')
8588
expect(res[1].filters[1].args.length).toBe(1)
86-
expect(res[1].filters[1].args[0]).toBe('l')
89+
expect(res[1].filters[1].args[0].value).toBe('l')
90+
expect(res[1].filters[1].args[0].dynamic).toBe(true)
8791

8892
expect(res[2].arg).toBe('g')
8993
expect(res[2].expression).toBe('h')
9094
expect(res[2].filters.length).toBe(1)
9195
expect(res[2].filters[0].name).toBe('i')
92-
expect(res[2].filters[0].args).toBeNull()
96+
expect(res[2].filters[0].args.length).toBe(1)
97+
expect(res[2].filters[0].args[0].value).toBe('k')
98+
expect(res[2].filters[0].args[0].dynamic).toBe(false)
9399
})
94100

95101
it('nexted function calls + array/object literals', function () {

test/unit/specs/parsers/text_spec.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,10 @@ describe('Text Parser', function () {
112112
it('tokens to expression with filters, multiple expressions', function () {
113113
var tokens = textParser.parse('a {{b | c d | f}} e')
114114
var exp = textParser.tokensToExp(tokens)
115-
expect(exp).toBe('"a "+this._applyFilter("f",[this._applyFilter("c",[b,"d"])])+" e"')
115+
expect(exp).toBe(
116+
'"a "+this._applyFilter("f",[this._applyFilter("c",[b,' +
117+
JSON.stringify({value:'d',dynamic:true}) +
118+
'])])+" e"')
116119
})
117120

118121
})

0 commit comments

Comments
 (0)