Skip to content

Commit d1f1eea

Browse files
Add new vue/no-use-v-else-with-v-for (#2224)
Co-authored-by: Yosuke Ota <[email protected]>
1 parent 2aa9ac4 commit d1f1eea

7 files changed

+206
-4
lines changed

docs/rules/index.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ Rules in this category are enabled for all presets provided by eslint-plugin-vue
8787
| [vue/no-unused-components](./no-unused-components.md) | disallow registering components that are not used inside templates | | :three::two::hammer: |
8888
| [vue/no-unused-vars](./no-unused-vars.md) | disallow unused variable definitions of v-for directives or scope attributes | :bulb: | :three::two::hammer: |
8989
| [vue/no-use-computed-property-like-method](./no-use-computed-property-like-method.md) | disallow use computed property like method | | :three::two::warning: |
90-
| [vue/no-use-v-if-with-v-for](./no-use-v-if-with-v-for.md) | disallow use v-if on the same element as v-for | | :three::two::hammer: |
90+
| [vue/no-use-v-if-with-v-for](./no-use-v-if-with-v-for.md) | disallow using `v-if` on the same element as `v-for` | | :three::two::hammer: |
9191
| [vue/no-useless-template-attributes](./no-useless-template-attributes.md) | disallow useless attribute on `<template>` | | :three::two::warning: |
9292
| [vue/no-v-for-template-key-on-child](./no-v-for-template-key-on-child.md) | disallow key of `<template v-for>` placed on child elements | | :three::warning: |
9393
| [vue/no-v-for-template-key](./no-v-for-template-key.md) | disallow `key` attribute on `<template v-for>` | | :two::warning: |
@@ -251,6 +251,7 @@ For example:
251251
| [vue/no-unsupported-features](./no-unsupported-features.md) | disallow unsupported Vue.js syntax on the specified version | :wrench: | :hammer: |
252252
| [vue/no-unused-properties](./no-unused-properties.md) | disallow unused properties | | :hammer: |
253253
| [vue/no-unused-refs](./no-unused-refs.md) | disallow unused refs | | :hammer: |
254+
| [vue/no-use-v-else-with-v-for](./no-use-v-else-with-v-for.md) | disallow using `v-else-if`/`v-else` on the same element as `v-for` | | :hammer: |
254255
| [vue/no-useless-mustaches](./no-useless-mustaches.md) | disallow unnecessary mustache interpolations | :wrench: | :hammer: |
255256
| [vue/no-useless-v-bind](./no-useless-v-bind.md) | disallow unnecessary `v-bind` directives | :wrench: | :hammer: |
256257
| [vue/no-v-text](./no-v-text.md) | disallow use of v-text | | :hammer: |
+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/no-use-v-else-with-v-for
5+
description: disallow using `v-else-if`/`v-else` on the same element as `v-for`
6+
---
7+
# vue/no-use-v-else-with-v-for
8+
9+
> disallow using `v-else-if`/`v-else` on the same element as `v-for`
10+
11+
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>
12+
13+
## :book: Rule Details
14+
15+
This rule reports elements that have both `v-else-if`/`v-else` and `v-for` directives. That is valid in Vue (`v-else-if`/`v-else` will take precedence), but is confusing to read.
16+
17+
<eslint-code-block :rules="{'vue/no-use-v-else-with-v-for': ['error']}">
18+
19+
```vue
20+
<template>
21+
<!-- ✓ GOOD -->
22+
<div v-if="foo">foo</div>
23+
<template v-else-if="bar">
24+
<div v-for="x in xs">{{ x }}</div>
25+
</template>
26+
<template v-else>
27+
<div v-for="x in xs">{{ x }}</div>
28+
</template>
29+
30+
<!-- ✗ BAD -->
31+
<div v-if="foo">foo</div>
32+
<div v-else-if="bar" v-for="x in xs">{{ x }}</div>
33+
<div v-else v-for="x in xs">{{ x }}</div>
34+
</template>
35+
```
36+
37+
</eslint-code-block>
38+
39+
## :wrench: Options
40+
41+
Nothing.
42+
43+
## :mute: When Not To Use It
44+
45+
If you don't find using `v-else-if`/`v-else` together with `v-for` confusing to read, you can safely disable this rule.
46+
47+
## :couple: Related Rules
48+
49+
- [vue/no-use-v-if-with-v-for](./no-use-v-if-with-v-for.md)
50+
51+
## :mag: Implementation
52+
53+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-use-v-else-with-v-for.js)
54+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-use-v-else-with-v-for.js)

docs/rules/no-use-v-if-with-v-for.md

+6-2
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
pageClass: rule-details
33
sidebarDepth: 0
44
title: vue/no-use-v-if-with-v-for
5-
description: disallow use v-if on the same element as v-for
5+
description: disallow using `v-if` on the same element as `v-for`
66
since: v4.6.0
77
---
88
# vue/no-use-v-if-with-v-for
99

10-
> disallow use v-if on the same element as v-for
10+
> disallow using `v-if` on the same element as `v-for`
1111
1212
- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/essential"`, `"plugin:vue/vue3-strongly-recommended"`, `"plugin:vue/strongly-recommended"`, `"plugin:vue/vue3-recommended"` and `"plugin:vue/recommended"`.
1313

@@ -88,6 +88,10 @@ There are two common cases where this can be tempting:
8888

8989
</eslint-code-block>
9090

91+
## :couple: Related Rules
92+
93+
- [vue/no-use-v-else-with-v-for](./no-use-v-else-with-v-for.md)
94+
9195
## :books: Further Reading
9296

9397
- [Style guide - Avoid v-if with v-for](https://vuejs.org/style-guide/rules-essential.html#avoid-v-if-with-v-for)

lib/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ module.exports = {
147147
'no-unused-refs': require('./rules/no-unused-refs'),
148148
'no-unused-vars': require('./rules/no-unused-vars'),
149149
'no-use-computed-property-like-method': require('./rules/no-use-computed-property-like-method'),
150+
'no-use-v-else-with-v-for': require('./rules/no-use-v-else-with-v-for'),
150151
'no-use-v-if-with-v-for': require('./rules/no-use-v-if-with-v-for'),
151152
'no-useless-concat': require('./rules/no-useless-concat'),
152153
'no-useless-mustaches': require('./rules/no-useless-mustaches'),

lib/rules/no-use-v-else-with-v-for.js

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
'use strict'
2+
3+
const { defineTemplateBodyVisitor, hasDirective } = require('../utils')
4+
5+
module.exports = {
6+
meta: {
7+
type: 'suggestion',
8+
docs: {
9+
description:
10+
'disallow using `v-else-if`/`v-else` on the same element as `v-for`',
11+
categories: null,
12+
url: 'https://eslint.vuejs.org/rules/no-use-v-else-with-v-for.html'
13+
},
14+
fixable: null,
15+
schema: [],
16+
messages: {
17+
unexpectedDirectiveWithVFor:
18+
'Unexpected `{{ directiveName }}` and `v-for` on the same element. Move `{{ directiveName }}` to a wrapper element instead.'
19+
}
20+
},
21+
/** @param {RuleContext} context */
22+
create(context) {
23+
return defineTemplateBodyVisitor(context, {
24+
/** @param {VDirective} node */
25+
"VAttribute[directive=true][key.name.name='for']"(node) {
26+
const element = node.parent.parent
27+
28+
if (hasDirective(element, 'else-if')) {
29+
context.report({
30+
node: element,
31+
messageId: 'unexpectedDirectiveWithVFor',
32+
data: { directiveName: 'v-else-if' }
33+
})
34+
} else if (hasDirective(element, 'else')) {
35+
context.report({
36+
node: element,
37+
messageId: 'unexpectedDirectiveWithVFor',
38+
data: { directiveName: 'v-else' }
39+
})
40+
}
41+
}
42+
})
43+
}
44+
}

lib/rules/no-use-v-if-with-v-for.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ module.exports = {
3838
meta: {
3939
type: 'suggestion',
4040
docs: {
41-
description: 'disallow use v-if on the same element as v-for',
41+
description: 'disallow using `v-if` on the same element as `v-for`',
4242
categories: ['vue3-essential', 'essential'],
4343
url: 'https://eslint.vuejs.org/rules/no-use-v-if-with-v-for.html'
4444
},
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
'use strict'
2+
3+
const RuleTester = require('eslint').RuleTester
4+
const rule = require('../../../lib/rules/no-use-v-else-with-v-for')
5+
6+
const tester = new RuleTester({
7+
parser: require.resolve('vue-eslint-parser'),
8+
parserOptions: { ecmaVersion: 2015 }
9+
})
10+
11+
tester.run('no-use-v-else-with-v-for', rule, {
12+
valid: [
13+
{
14+
// caught by `vue/no-use-v-if-with-v-for`
15+
filename: 'test.vue',
16+
code: `
17+
<template>
18+
<div v-if="foo" v-for="x in xs">{{ x }}</div>
19+
</template>
20+
`
21+
},
22+
{
23+
// `v-if`/`v-else-if`/`v-else` only
24+
filename: 'test.vue',
25+
code: `
26+
<template>
27+
<div v-if="foo">{{ x }}</div>
28+
<div v-else-if="foo">{{ x }}</div>
29+
<div v-else="foo">{{ x }}</div>
30+
</template>
31+
`
32+
},
33+
{
34+
// `v-for` only
35+
filename: 'test.vue',
36+
code: `
37+
<template>
38+
<div v-for="x in xs">{{ x }}</div>
39+
</template>
40+
`
41+
},
42+
{
43+
// `v-else-if`/`v-else` in template + `v-for`
44+
filename: 'test.vue',
45+
code: `
46+
<template>
47+
<div v-if="foo">foo</div>
48+
<template v-else-if="bar">
49+
<div v-for="x in xs">{{ x }}</div>
50+
</template>
51+
<template v-else>
52+
<div v-for="x in xs">{{ x }}</div>
53+
</template>
54+
</template>
55+
`
56+
}
57+
],
58+
invalid: [
59+
{
60+
filename: 'test.vue',
61+
code: `
62+
<template>
63+
<div v-if="foo">foo</div>
64+
<div v-else v-for="x in xs">{{ x }}</div>
65+
</template>
66+
`,
67+
errors: [
68+
{
69+
message:
70+
'Unexpected `v-else` and `v-for` on the same element. Move `v-else` to a wrapper element instead.',
71+
line: 4,
72+
endLine: 4,
73+
column: 11,
74+
endColumn: 52
75+
}
76+
]
77+
},
78+
{
79+
filename: 'test.vue',
80+
code: `
81+
<template>
82+
<div v-if="foo">foo</div>
83+
<div v-else-if="bar" v-for="x in xs">{{ x }}</div>
84+
</template>
85+
`,
86+
errors: [
87+
{
88+
message:
89+
'Unexpected `v-else-if` and `v-for` on the same element. Move `v-else-if` to a wrapper element instead.',
90+
line: 4,
91+
endLine: 4,
92+
column: 11,
93+
endColumn: 61
94+
}
95+
]
96+
}
97+
]
98+
})

0 commit comments

Comments
 (0)