Skip to content
2 changes: 1 addition & 1 deletion src/api/directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@

This directive triggers transitions when its condition changes.

When used together with `v-if`, `v-for` has a higher priority than v-if. See the [list rendering guide](../guide/list.html#v-for-with-v-if) for details.
When used together, `v-if` has a higher priority than `v-for`. We don't recommend using these two directives together on one element — see the [list rendering guide](../guide/list.html#v-for-with-v-if) for details.

- **See also:** [Conditional Rendering - v-if](../guide/conditional.html#v-if)

Expand Down
2 changes: 1 addition & 1 deletion src/guide/conditional.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,4 @@ Generally speaking, `v-if` has higher toggle costs while `v-show` has higher ini
Using `v-if` and `v-for` together is **not recommended**. See the [style guide](../style-guide/#avoid-v-if-with-v-for-essential) for further information.
:::

When used together with `v-if`, `v-for` has a higher priority than `v-if`. See the [list rendering guide](list#v-for-with-v-if) for details.
When `v-if` and `v-for` are both used on the same element, `v-if` will be evaluated first. See the [list rendering guide](list#v-for-with-v-if) for details.
15 changes: 7 additions & 8 deletions src/guide/list.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,25 +268,24 @@ Similar to template `v-if`, you can also use a `<template>` tag with `v-for` to
Note that it's **not** recommended to use `v-if` and `v-for` together. Refer to [style guide](../style-guide/#avoid-v-if-with-v-for-essential) for details.
:::

When they exist on the same node, `v-for` has a higher priority than `v-if`. That means the `v-if` will be run on each iteration of the loop separately. This can be useful when you want to render nodes for only _some_ items, like below:
When they exist on the same node, `v-if` has a higher priority than `v-for`. That means the `v-if` condition will not have access to variables from the scope of the `v-for`:

```html
<!-- This will throw an error because property "todo" is not defined on instance. -->

<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo }}
</li>
```

The above only renders the todos that are not complete.

If instead, your intent is to conditionally skip execution of the loop, you can place the `v-if` on a wrapper element (or [`<template>`](conditional#conditional-groups-with-v-if-on-lt-template-gt)). For example:
This can be fixed by moving `v-for` to a wrapping `<template>` tag:

```html
<ul v-if="todos.length">
<li v-for="todo in todos">
<template v-for="todo in todos">
<li v-if="!todo.isComplete">
{{ todo }}
</li>
</ul>
<p v-else>No todos left!</p>
</template>
```

## `v-for` with a Component
Expand Down
79 changes: 17 additions & 62 deletions src/style-guide/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ There are two common cases where this can be tempting:
- To avoid rendering a list if it should be hidden (e.g. `v-for="user in users" v-if="shouldShowUsers"`). In these cases, move the `v-if` to a container element (e.g. `ul`, `ol`).

::: details Detailed Explanation
When Vue processes directives, `v-for` has a higher priority than `v-if`, so that this template:
When Vue processes directives, `v-if` has a higher priority than `v-for`, so that this template:

``` html
<ul>
Expand All @@ -210,19 +210,9 @@ When Vue processes directives, `v-for` has a higher priority than `v-if`, so tha
</ul>
```

Will be evaluated similar to:
Will throw an error, because the `v-if` directive will be evaluated first and the iteration variable `user` does not exist at this moment.

``` js
this.users.map(user => {
if (user.isActive) {
return user.name
}
})
```

So even if we only render elements for a small fraction of users, we have to iterate over the entire list every time we re-render, whether or not the set of active users has changed.

By iterating over a computed property instead, like this:
This could be fixed by iterating over a computed property instead, like this:

``` js
computed: {
Expand All @@ -243,40 +233,18 @@ computed: {
</ul>
```

We get the following benefits:
Alternatively, we can use a `<template>` tag with `v-for` to wrap the `<li>` element:

- The filtered list will _only_ be re-evaluated if there are relevant changes to the `users` array, making filtering much more efficient.
- Using `v-for="user in activeUsers"`, we _only_ iterate over active users during render, making rendering much more efficient.
- Logic is now decoupled from the presentation layer, making maintenance (change/extension of logic) much easier.

We get similar benefits from updating:

``` html
```html
<ul>
<li
v-for="user in users"
v-if="shouldShowUsers"
:key="user.id"
>
{{ user.name }}
</li>
<template v-for="user in users" :key="user.id">
<li v-if="user.isActive">
{{ user.name }}
</li>
</template>
</ul>
```

to:

``` html
<ul v-if="shouldShowUsers">
<li
v-for="user in users"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
```

By moving the `v-if` to a container element, we're no longer checking `shouldShowUsers` for _every_ user in the list. Instead, we check it once and don't even evaluate the `v-for` if `shouldShowUsers` is false.
:::

<div class="style-example style-example-bad">
Expand All @@ -293,18 +261,6 @@ By moving the `v-if` to a container element, we're no longer checking `shouldSho
</li>
</ul>
```

``` html
<ul>
<li
v-for="user in users"
v-if="shouldShowUsers"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
```
</div>

<div class="style-example style-example-good">
Expand All @@ -321,14 +277,13 @@ By moving the `v-if` to a container element, we're no longer checking `shouldSho
</ul>
```

``` html
<ul v-if="shouldShowUsers">
<li
v-for="user in users"
:key="user.id"
>
{{ user.name }}
</li>
```html
<ul>
<template v-for="user in users" :key="user.id">
<li v-if="user.isActive">
{{ user.name }}
</li>
</template>
</ul>
```
</div>
Expand Down