File tree 3 files changed +50
-2
lines changed
3 files changed +50
-2
lines changed Original file line number Diff line number Diff line change @@ -263,6 +263,34 @@ describe('compiler: v-for', () => {
263
263
} )
264
264
)
265
265
} )
266
+
267
+ test ( '<template v-for> key placement' , ( ) => {
268
+ const onError = jest . fn ( )
269
+ parseWithForTransform (
270
+ `
271
+ <template v-for="item in items">
272
+ <div :key="item.id"/>
273
+ </template>` ,
274
+ { onError }
275
+ )
276
+
277
+ expect ( onError ) . toHaveBeenCalledTimes ( 1 )
278
+ expect ( onError ) . toHaveBeenCalledWith (
279
+ expect . objectContaining ( {
280
+ code : ErrorCodes . X_V_FOR_TEMPLATE_KEY_PLACEMENT
281
+ } )
282
+ )
283
+
284
+ // should not warn on nested v-for keys
285
+ parseWithForTransform (
286
+ `
287
+ <template v-for="item in items">
288
+ <div v-for="c in item.children" :key="c.id"/>
289
+ </template>` ,
290
+ { onError }
291
+ )
292
+ expect ( onError ) . toHaveBeenCalledTimes ( 1 )
293
+ } )
266
294
} )
267
295
268
296
describe ( 'source location' , ( ) => {
Original file line number Diff line number Diff line change @@ -67,6 +67,7 @@ export const enum ErrorCodes {
67
67
X_V_ELSE_NO_ADJACENT_IF ,
68
68
X_V_FOR_NO_EXPRESSION ,
69
69
X_V_FOR_MALFORMED_EXPRESSION ,
70
+ X_V_FOR_TEMPLATE_KEY_PLACEMENT ,
70
71
X_V_BIND_NO_EXPRESSION ,
71
72
X_V_ON_NO_EXPRESSION ,
72
73
X_V_SLOT_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET ,
@@ -140,6 +141,7 @@ export const errorMessages: { [code: number]: string } = {
140
141
[ ErrorCodes . X_V_ELSE_NO_ADJACENT_IF ] : `v-else/v-else-if has no adjacent v-if.` ,
141
142
[ ErrorCodes . X_V_FOR_NO_EXPRESSION ] : `v-for is missing expression.` ,
142
143
[ ErrorCodes . X_V_FOR_MALFORMED_EXPRESSION ] : `v-for has invalid expression.` ,
144
+ [ ErrorCodes . X_V_FOR_TEMPLATE_KEY_PLACEMENT ] : `<template v-for> key should be placed on the <template> tag.` ,
143
145
[ ErrorCodes . X_V_BIND_NO_EXPRESSION ] : `v-bind is missing expression.` ,
144
146
[ ErrorCodes . X_V_ON_NO_EXPRESSION ] : `v-on is missing expression.` ,
145
147
[ ErrorCodes . X_V_SLOT_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET ] : `Unexpected custom directive on <slot> outlet.` ,
Original file line number Diff line number Diff line change @@ -10,7 +10,6 @@ import {
10
10
SimpleExpressionNode ,
11
11
createCallExpression ,
12
12
createFunctionExpression ,
13
- ElementTypes ,
14
13
createObjectExpression ,
15
14
createObjectProperty ,
16
15
ForCodegenNode ,
@@ -81,6 +80,25 @@ export const transformFor = createStructuralDirectiveTransform(
81
80
let childBlock : BlockCodegenNode
82
81
const isTemplate = isTemplateNode ( node )
83
82
const { children } = forNode
83
+
84
+ // check <template v-for> key placement
85
+ if ( ( __DEV__ || ! __BROWSER__ ) && isTemplate ) {
86
+ node . children . some ( c => {
87
+ if ( c . type === NodeTypes . ELEMENT ) {
88
+ const key = findProp ( c , 'key' )
89
+ if ( key ) {
90
+ context . onError (
91
+ createCompilerError (
92
+ ErrorCodes . X_V_FOR_TEMPLATE_KEY_PLACEMENT ,
93
+ key . loc
94
+ )
95
+ )
96
+ return true
97
+ }
98
+ }
99
+ } )
100
+ }
101
+
84
102
const needFragmentWrapper =
85
103
children . length !== 1 || children [ 0 ] . type !== NodeTypes . ELEMENT
86
104
const slotOutlet = isSlotOutlet ( node )
@@ -183,7 +201,7 @@ export function processFor(
183
201
keyAlias : key ,
184
202
objectIndexAlias : index ,
185
203
parseResult,
186
- children : node . tagType === ElementTypes . TEMPLATE ? node . children : [ node ]
204
+ children : isTemplateNode ( node ) ? node . children : [ node ]
187
205
}
188
206
189
207
context . replaceNode ( forNode )
You can’t perform that action at this time.
0 commit comments