Skip to content

Commit cec833a

Browse files
committed
implement new v-for iterator syntax (ref: #3073)
1 parent 9842f3e commit cec833a

File tree

8 files changed

+154
-124
lines changed

8 files changed

+154
-124
lines changed

examples/svg/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
<polygon :points="points"></polygon>
1515
<circle cx="100" cy="100" r="80"></circle>
1616
<axis-label
17-
v-for="stat in stats"
17+
v-for="(stat, index) in stats"
1818
:stat="stat"
19-
:index="$index"
19+
:index="index"
2020
:total="stats.length">
2121
</axis-label>
2222
</g>

flow/compiler.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ declare type ASTElement = {
9494
for?: string | null,
9595
key?: string,
9696
alias?: string,
97-
iterator?: string,
97+
iterator1?: string,
98+
iterator2?: string,
9899

99100
staticClass?: string,
100101
classBinding?: string,

src/compiler/codegen.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,11 @@ function genElse (el: ASTElement): string {
101101
function genFor (el: ASTElement): string {
102102
const exp = el.for
103103
const alias = el.alias
104-
const iterator = el.iterator
104+
const iterator1 = el.iterator1 ? `,${el.iterator1}` : ''
105+
const iterator2 = el.iterator2 ? `,${el.iterator2}` : ''
105106
el.for = null // avoid recursion
106107
return `(${exp})&&_l((${exp}),` +
107-
`function(${alias},$index,${iterator || '$key'}){` +
108+
`function(${alias}${iterator1}${iterator2}){` +
108109
`return ${genElement(el)}` +
109110
'})'
110111
}

src/compiler/parser/index.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const onRE = /^@|^v-on:/
2222
const argRE = /:(.*)$/
2323
const modifierRE = /\.[^\.]+/g
2424
const forAliasRE = /(.*)\s+(?:in|of)\s+(.*)/
25-
const forIteratorRE = /\((.*),(.*)\)/
25+
const forIteratorRE = /\(([^,]*),([^,]*)(?:,([^,]*))?\)/
2626
const camelRE = /[a-z\d][A-Z]/
2727

2828
const decodeHTMLCached = cached(decodeHTML)
@@ -266,8 +266,11 @@ function processFor (el) {
266266
const alias = inMatch[1].trim()
267267
const iteratorMatch = alias.match(forIteratorRE)
268268
if (iteratorMatch) {
269-
el.iterator = iteratorMatch[1].trim()
270-
el.alias = iteratorMatch[2].trim()
269+
el.alias = iteratorMatch[1].trim()
270+
el.iterator1 = iteratorMatch[2].trim()
271+
if (iteratorMatch[3]) {
272+
el.iterator2 = iteratorMatch[3].trim()
273+
}
271274
} else {
272275
el.alias = alias
273276
}

src/core/instance/render.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,19 +112,19 @@ export function renderMixin (Vue: Class<Component>) {
112112
if (Array.isArray(val)) {
113113
ret = new Array(val.length)
114114
for (i = 0, l = val.length; i < l; i++) {
115-
ret[i] = render(val[i], i, i)
115+
ret[i] = render(val[i], i)
116116
}
117117
} else if (typeof val === 'number') {
118118
ret = new Array(val)
119119
for (i = 0; i < val; i++) {
120-
ret[i] = render(i + 1, i, i)
120+
ret[i] = render(i + 1, i)
121121
}
122122
} else if (isObject(val)) {
123123
keys = Object.keys(val)
124124
ret = new Array(keys.length)
125125
for (i = 0, l = keys.length; i < l; i++) {
126126
key = keys[i]
127-
ret[i] = render(val[key], i, key)
127+
ret[i] = render(val[key], key, i)
128128
}
129129
}
130130
return ret

test/unit/features/directives/for.spec.js

Lines changed: 115 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,34 @@ describe('Directive v-for', () => {
55
const vm = new Vue({
66
template: `
77
<div>
8-
<span v-for="item in list">{{$index}}-{{item}}</span>
8+
<span v-for="item in list">{{item}}</span>
9+
</div>
10+
`,
11+
data: {
12+
list: ['a', 'b', 'c']
13+
}
14+
}).$mount()
15+
expect(vm.$el.innerHTML).toBe('<span>a</span><span>b</span><span>c</span>')
16+
Vue.set(vm.list, 0, 'd')
17+
waitForUpdate(() => {
18+
expect(vm.$el.innerHTML).toBe('<span>d</span><span>b</span><span>c</span>')
19+
vm.list.push('d')
20+
}).then(() => {
21+
expect(vm.$el.innerHTML).toBe('<span>d</span><span>b</span><span>c</span><span>d</span>')
22+
vm.list.splice(1, 2)
23+
}).then(() => {
24+
expect(vm.$el.innerHTML).toBe('<span>d</span><span>d</span>')
25+
vm.list = ['x', 'y']
26+
}).then(() => {
27+
expect(vm.$el.innerHTML).toBe('<span>x</span><span>y</span>')
28+
}).then(done)
29+
})
30+
31+
it('should render array of primitive values with index', done => {
32+
const vm = new Vue({
33+
template: `
34+
<div>
35+
<span v-for="(item, i) in list">{{i}}-{{item}}</span>
936
</div>
1037
`,
1138
data: {
@@ -32,7 +59,41 @@ describe('Directive v-for', () => {
3259
const vm = new Vue({
3360
template: `
3461
<div>
35-
<span v-for="item in list">{{$index}}-{{item.value}}</span>
62+
<span v-for="item in list">{{item.value}}</span>
63+
</div>
64+
`,
65+
data: {
66+
list: [
67+
{ value: 'a' },
68+
{ value: 'b' },
69+
{ value: 'c' }
70+
]
71+
}
72+
}).$mount()
73+
expect(vm.$el.innerHTML).toBe('<span>a</span><span>b</span><span>c</span>')
74+
Vue.set(vm.list, 0, { value: 'd' })
75+
waitForUpdate(() => {
76+
expect(vm.$el.innerHTML).toBe('<span>d</span><span>b</span><span>c</span>')
77+
vm.list[0].value = 'e'
78+
}).then(() => {
79+
expect(vm.$el.innerHTML).toBe('<span>e</span><span>b</span><span>c</span>')
80+
vm.list.push({})
81+
}).then(() => {
82+
expect(vm.$el.innerHTML).toBe('<span>e</span><span>b</span><span>c</span><span></span>')
83+
vm.list.splice(1, 2)
84+
}).then(() => {
85+
expect(vm.$el.innerHTML).toBe('<span>e</span><span></span>')
86+
vm.list = [{ value: 'x' }, { value: 'y' }]
87+
}).then(() => {
88+
expect(vm.$el.innerHTML).toBe('<span>x</span><span>y</span>')
89+
}).then(done)
90+
})
91+
92+
it('should render array of object values with index', done => {
93+
const vm = new Vue({
94+
template: `
95+
<div>
96+
<span v-for="(item, i) in list">{{i}}-{{item.value}}</span>
3697
</div>
3798
`,
3899
data: {
@@ -66,7 +127,31 @@ describe('Directive v-for', () => {
66127
const vm = new Vue({
67128
template: `
68129
<div>
69-
<span v-for="val in obj">{{val}}-{{$key}}</span>
130+
<span v-for="val in obj">{{val}}</span>
131+
</div>
132+
`,
133+
data: {
134+
obj: { a: 0, b: 1, c: 2 }
135+
}
136+
}).$mount()
137+
expect(vm.$el.innerHTML).toBe('<span>0</span><span>1</span><span>2</span>')
138+
vm.obj.a = 3
139+
waitForUpdate(() => {
140+
expect(vm.$el.innerHTML).toBe('<span>3</span><span>1</span><span>2</span>')
141+
Vue.set(vm.obj, 'd', 4)
142+
}).then(() => {
143+
expect(vm.$el.innerHTML).toBe('<span>3</span><span>1</span><span>2</span><span>4</span>')
144+
Vue.delete(vm.obj, 'a')
145+
}).then(() => {
146+
expect(vm.$el.innerHTML).toBe('<span>1</span><span>2</span><span>4</span>')
147+
}).then(done)
148+
})
149+
150+
it('should render an Object with key', done => {
151+
const vm = new Vue({
152+
template: `
153+
<div>
154+
<span v-for="(val, key) in obj">{{val}}-{{key}}</span>
70155
</div>
71156
`,
72157
data: {
@@ -86,11 +171,35 @@ describe('Directive v-for', () => {
86171
}).then(done)
87172
})
88173

174+
it('should render an Object with key and index', done => {
175+
const vm = new Vue({
176+
template: `
177+
<div>
178+
<span v-for="(val, key, i) in obj">{{val}}-{{key}}-{{i}}</span>
179+
</div>
180+
`,
181+
data: {
182+
obj: { a: 0, b: 1, c: 2 }
183+
}
184+
}).$mount()
185+
expect(vm.$el.innerHTML).toBe('<span>0-a-0</span><span>1-b-1</span><span>2-c-2</span>')
186+
vm.obj.a = 3
187+
waitForUpdate(() => {
188+
expect(vm.$el.innerHTML).toBe('<span>3-a-0</span><span>1-b-1</span><span>2-c-2</span>')
189+
Vue.set(vm.obj, 'd', 4)
190+
}).then(() => {
191+
expect(vm.$el.innerHTML).toBe('<span>3-a-0</span><span>1-b-1</span><span>2-c-2</span><span>4-d-3</span>')
192+
Vue.delete(vm.obj, 'a')
193+
}).then(() => {
194+
expect(vm.$el.innerHTML).toBe('<span>1-b-0</span><span>2-c-1</span><span>4-d-2</span>')
195+
}).then(done)
196+
})
197+
89198
it('should render each key of data', done => {
90199
const vm = new Vue({
91200
template: `
92201
<div>
93-
<span v-for="val in $data">{{val}}-{{$key}}</span>
202+
<span v-for="(val, key) in $data">{{val}}-{{key}}</span>
94203
</div>
95204
`,
96205
data: { a: 0, b: 1, c: 2 }
@@ -102,109 +211,6 @@ describe('Directive v-for', () => {
102211
}).then(done)
103212
})
104213

105-
describe('alternative syntax', () => {
106-
it('should render array of primitive values', done => {
107-
const vm = new Vue({
108-
template: `
109-
<div>
110-
<span v-for="(i, item) in list">{{i}}-{{item}}</span>
111-
</div>
112-
`,
113-
data: {
114-
list: ['a', 'b', 'c']
115-
}
116-
}).$mount()
117-
expect(vm.$el.innerHTML).toBe('<span>0-a</span><span>1-b</span><span>2-c</span>')
118-
Vue.set(vm.list, 0, 'd')
119-
waitForUpdate(() => {
120-
expect(vm.$el.innerHTML).toBe('<span>0-d</span><span>1-b</span><span>2-c</span>')
121-
vm.list.push('d')
122-
}).then(() => {
123-
expect(vm.$el.innerHTML).toBe('<span>0-d</span><span>1-b</span><span>2-c</span><span>3-d</span>')
124-
vm.list.splice(1, 2)
125-
}).then(() => {
126-
expect(vm.$el.innerHTML).toBe('<span>0-d</span><span>1-d</span>')
127-
vm.list = ['x', 'y']
128-
}).then(() => {
129-
expect(vm.$el.innerHTML).toBe('<span>0-x</span><span>1-y</span>')
130-
}).then(done)
131-
})
132-
133-
it('should render array of object values', done => {
134-
const vm = new Vue({
135-
template: `
136-
<div>
137-
<span v-for="(i, item) in list">{{i}}-{{item.value}}</span>
138-
</div>
139-
`,
140-
data: {
141-
list: [
142-
{ value: 'a' },
143-
{ value: 'b' },
144-
{ value: 'c' }
145-
]
146-
}
147-
}).$mount()
148-
expect(vm.$el.innerHTML).toBe('<span>0-a</span><span>1-b</span><span>2-c</span>')
149-
Vue.set(vm.list, 0, { value: 'd' })
150-
waitForUpdate(() => {
151-
expect(vm.$el.innerHTML).toBe('<span>0-d</span><span>1-b</span><span>2-c</span>')
152-
vm.list[0].value = 'e'
153-
}).then(() => {
154-
expect(vm.$el.innerHTML).toBe('<span>0-e</span><span>1-b</span><span>2-c</span>')
155-
vm.list.push({})
156-
}).then(() => {
157-
expect(vm.$el.innerHTML).toBe('<span>0-e</span><span>1-b</span><span>2-c</span><span>3-</span>')
158-
vm.list.splice(1, 2)
159-
}).then(() => {
160-
expect(vm.$el.innerHTML).toBe('<span>0-e</span><span>1-</span>')
161-
vm.list = [{ value: 'x' }, { value: 'y' }]
162-
}).then(() => {
163-
expect(vm.$el.innerHTML).toBe('<span>0-x</span><span>1-y</span>')
164-
}).then(done)
165-
})
166-
167-
it('should render an Object', done => {
168-
const vm = new Vue({
169-
template: `
170-
<div>
171-
<span v-for="(k, v) in obj">{{v}}-{{k}}</span>
172-
</div>
173-
`,
174-
data: {
175-
obj: { a: 0, b: 1, c: 2 }
176-
}
177-
}).$mount()
178-
expect(vm.$el.innerHTML).toBe('<span>0-a</span><span>1-b</span><span>2-c</span>')
179-
vm.obj.a = 3
180-
waitForUpdate(() => {
181-
expect(vm.$el.innerHTML).toBe('<span>3-a</span><span>1-b</span><span>2-c</span>')
182-
Vue.set(vm.obj, 'd', 4)
183-
}).then(() => {
184-
expect(vm.$el.innerHTML).toBe('<span>3-a</span><span>1-b</span><span>2-c</span><span>4-d</span>')
185-
Vue.delete(vm.obj, 'a')
186-
}).then(() => {
187-
expect(vm.$el.innerHTML).toBe('<span>1-b</span><span>2-c</span><span>4-d</span>')
188-
}).then(done)
189-
})
190-
191-
it('should render each key of data', done => {
192-
const vm = new Vue({
193-
template: `
194-
<div>
195-
<span v-for="(k, v) in $data">{{v}}-{{k}}</span>
196-
</div>
197-
`,
198-
data: { a: 0, b: 1, c: 2 }
199-
}).$mount()
200-
expect(vm.$el.innerHTML).toBe('<span>0-a</span><span>1-b</span><span>2-c</span>')
201-
vm.a = 3
202-
waitForUpdate(() => {
203-
expect(vm.$el.innerHTML).toBe('<span>3-a</span><span>1-b</span><span>2-c</span>')
204-
}).then(done)
205-
})
206-
})
207-
208214
it('check priorities: v-if before v-for', function () {
209215
const vm = new Vue({
210216
data: {
@@ -285,8 +291,8 @@ describe('Directive v-for', () => {
285291
},
286292
template:
287293
'<div>' +
288-
'<div v-for="(i, item) in items">' +
289-
'<p v-for="subItem in item.items">{{$index}} {{subItem.a}} {{i}} {{item.a}}</p>' +
294+
'<div v-for="(item, i) in items">' +
295+
'<p v-for="(subItem, j) in item.items">{{j}} {{subItem.a}} {{i}} {{item.a}}</p>' +
290296
'</div>' +
291297
'</div>'
292298
}).$mount()

test/unit/modules/compiler/codegen.spec.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,16 @@ describe('codegen', () => {
4141
it('generate v-for directive', () => {
4242
assertCodegen(
4343
'<li v-for="item in items" :key="item.uid"></li>',
44-
`with(this){return (items)&&_l((items),function(item,$index,$key){return _h(_e('li',{key:item.uid}))})}`
44+
`with(this){return (items)&&_l((items),function(item){return _h(_e('li',{key:item.uid}))})}`
45+
)
46+
// iterator syntax
47+
assertCodegen(
48+
'<li v-for="(item, i) in items"></li>',
49+
`with(this){return (items)&&_l((items),function(item,i){return _h(_e('li'))})}`
50+
)
51+
assertCodegen(
52+
'<li v-for="(item, key, index) in items"></li>',
53+
`with(this){return (items)&&_l((items),function(item,key,index){return _h(_e('li'))})}`
4554
)
4655
})
4756

@@ -69,7 +78,7 @@ describe('codegen', () => {
6978
it('generate v-ref directive on v-for', () => {
7079
assertCodegen(
7180
'<ul><li v-for="item in items" v-ref:component1></li></ul>',
72-
`with(this){return _h(_e('ul'),[(items)&&_l((items),function(item,$index,$key){return _h(_e('li',{ref:"component1",refInFor:true}))})])}`
81+
`with(this){return _h(_e('ul'),[(items)&&_l((items),function(item){return _h(_e('li',{ref:"component1",refInFor:true}))})])}`
7382
)
7483
})
7584

0 commit comments

Comments
 (0)