Skip to content

Commit f5ce6b5

Browse files
committed
fix(v-for): support array and nested destructuring in v-for
1 parent d4e0c3e commit f5ce6b5

File tree

3 files changed

+92
-8
lines changed

3 files changed

+92
-8
lines changed

src/compiler/parser/index.js

+7-7
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
export const onRE = /^@|^v-on:/
2323
export const dirRE = /^v-|^@|^:/
2424
export const forAliasRE = /(.*?)\s+(?:in|of)\s+(.*)/
25-
export const forIteratorRE = /\((\{[^}]*\}|[^,{]*),([^,]*)(?:,([^,]*))?\)/
25+
export const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/
2626
const stripParensRE = /^\(|\)$/g
2727

2828
const argRE = /:(.*)$/
@@ -356,16 +356,16 @@ export function processFor (el: ASTElement) {
356356
return
357357
}
358358
el.for = inMatch[2].trim()
359-
const alias = inMatch[1].trim()
359+
const alias = inMatch[1].trim().replace(stripParensRE, '')
360360
const iteratorMatch = alias.match(forIteratorRE)
361361
if (iteratorMatch) {
362-
el.alias = iteratorMatch[1].trim()
363-
el.iterator1 = iteratorMatch[2].trim()
364-
if (iteratorMatch[3]) {
365-
el.iterator2 = iteratorMatch[3].trim()
362+
el.alias = alias.replace(forIteratorRE, '')
363+
el.iterator1 = iteratorMatch[1].trim()
364+
if (iteratorMatch[2]) {
365+
el.iterator2 = iteratorMatch[2].trim()
366366
}
367367
} else {
368-
el.alias = alias.replace(stripParensRE, '')
368+
el.alias = alias
369369
}
370370
}
371371
}

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

+9-1
Original file line numberDiff line numberDiff line change
@@ -472,12 +472,20 @@ describe('Directive v-for', () => {
472472
})()
473473

474474
if (supportsDestructuring) {
475-
it('should support destructuring syntax in alias position', () => {
475+
it('should support destructuring syntax in alias position (object)', () => {
476476
const vm = new Vue({
477477
data: { list: [{ foo: 'hi', bar: 'ho' }] },
478478
template: '<div><div v-for="({ foo, bar }, i) in list">{{ foo }} {{ bar }} {{ i }}</div></div>'
479479
}).$mount()
480480
expect(vm.$el.textContent).toBe('hi ho 0')
481481
})
482+
483+
it('should support destructuring syntax in alias position (array)', () => {
484+
const vm = new Vue({
485+
data: { list: [[1, 2], [3, 4]] },
486+
template: '<div><div v-for="([ foo, bar ], i) in list">{{ foo }} {{ bar }} {{ i }}</div></div>'
487+
}).$mount()
488+
expect(vm.$el.textContent).toBe('1 2 03 4 1')
489+
})
482490
}
483491
})

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

+76
Original file line numberDiff line numberDiff line change
@@ -314,12 +314,88 @@ describe('parser', () => {
314314
expect(liAst.alias).toBe('{ foo }')
315315
expect(liAst.iterator1).toBe('i')
316316

317+
// with key + index
318+
ast = parse('<ul><li v-for="({ foo }, i, j) in items"></li></ul>', baseOptions)
319+
liAst = ast.children[0]
320+
expect(liAst.for).toBe('items')
321+
expect(liAst.alias).toBe('{ foo }')
322+
expect(liAst.iterator1).toBe('i')
323+
expect(liAst.iterator2).toBe('j')
324+
317325
// multi-var destructuring with index
318326
ast = parse('<ul><li v-for="({ foo, bar, baz }, i) in items"></li></ul>', baseOptions)
319327
liAst = ast.children[0]
320328
expect(liAst.for).toBe('items')
321329
expect(liAst.alias).toBe('{ foo, bar, baz }')
322330
expect(liAst.iterator1).toBe('i')
331+
332+
// array
333+
ast = parse('<ul><li v-for="[ foo ] in items"></li></ul>', baseOptions)
334+
liAst = ast.children[0]
335+
expect(liAst.for).toBe('items')
336+
expect(liAst.alias).toBe('[ foo ]')
337+
338+
// multi-array
339+
ast = parse('<ul><li v-for="[ foo, bar, baz ] in items"></li></ul>', baseOptions)
340+
liAst = ast.children[0]
341+
expect(liAst.for).toBe('items')
342+
expect(liAst.alias).toBe('[ foo, bar, baz ]')
343+
344+
// array with paren
345+
ast = parse('<ul><li v-for="([ foo ]) in items"></li></ul>', baseOptions)
346+
liAst = ast.children[0]
347+
expect(liAst.for).toBe('items')
348+
expect(liAst.alias).toBe('[ foo ]')
349+
350+
// multi-array with paren
351+
ast = parse('<ul><li v-for="([ foo, bar, baz ]) in items"></li></ul>', baseOptions)
352+
liAst = ast.children[0]
353+
expect(liAst.for).toBe('items')
354+
expect(liAst.alias).toBe('[ foo, bar, baz ]')
355+
356+
// array with index
357+
ast = parse('<ul><li v-for="([ foo ], i) in items"></li></ul>', baseOptions)
358+
liAst = ast.children[0]
359+
expect(liAst.for).toBe('items')
360+
expect(liAst.alias).toBe('[ foo ]')
361+
expect(liAst.iterator1).toBe('i')
362+
363+
// array with key + index
364+
ast = parse('<ul><li v-for="([ foo ], i, j) in items"></li></ul>', baseOptions)
365+
liAst = ast.children[0]
366+
expect(liAst.for).toBe('items')
367+
expect(liAst.alias).toBe('[ foo ]')
368+
expect(liAst.iterator1).toBe('i')
369+
expect(liAst.iterator2).toBe('j')
370+
371+
// multi-array with paren
372+
ast = parse('<ul><li v-for="([ foo, bar, baz ]) in items"></li></ul>', baseOptions)
373+
liAst = ast.children[0]
374+
expect(liAst.for).toBe('items')
375+
expect(liAst.alias).toBe('[ foo, bar, baz ]')
376+
377+
// multi-array with index
378+
ast = parse('<ul><li v-for="([ foo, bar, baz ], i) in items"></li></ul>', baseOptions)
379+
liAst = ast.children[0]
380+
expect(liAst.for).toBe('items')
381+
expect(liAst.alias).toBe('[ foo, bar, baz ]')
382+
expect(liAst.iterator1).toBe('i')
383+
384+
// nested
385+
ast = parse('<ul><li v-for="({ foo, bar: { baz }, qux: [ n ] }, i, j) in items"></li></ul>', baseOptions)
386+
liAst = ast.children[0]
387+
expect(liAst.for).toBe('items')
388+
expect(liAst.alias).toBe('{ foo, bar: { baz }, qux: [ n ] }')
389+
expect(liAst.iterator1).toBe('i')
390+
expect(liAst.iterator2).toBe('j')
391+
392+
// array nested
393+
ast = parse('<ul><li v-for="([ foo, { bar }, baz ], i, j) in items"></li></ul>', baseOptions)
394+
liAst = ast.children[0]
395+
expect(liAst.for).toBe('items')
396+
expect(liAst.alias).toBe('[ foo, { bar }, baz ]')
397+
expect(liAst.iterator1).toBe('i')
398+
expect(liAst.iterator2).toBe('j')
323399
})
324400

325401
it('v-for directive invalid syntax', () => {

0 commit comments

Comments
 (0)