概要
Vueの算出プロパティであるcomputedは引数を持つことができず、v-forで回しているArray[Index]のような一部の要素のみを指定することができない。
一応、methodsに書いて呼び出すことで擬似的に動作させることは可能だが、状態が変わったときに再計算が行われないのが結構落とし穴である。
以下のコードは、コメント一覧画面でv-forでArrayのコメント一覧を回し、コメント1つ1つに対して閲覧者であるユーザのコメントがあったらそのコメントのみModifierのclassを付与したいという場合のサンプルコードである。
動作しないコード
computedは引数を持つことができないので、以下のコードはエラーとなる。
Comments.Vue
<template>
<div v-for="(comment, comments)" class="comments">
<p class="comments__comment" :class="isUserPostClass(comment)"/>
</div>
</temlate>
~省略
computed: {
isUserPostClass(comment) {
return comment.isUserPost ? 'comments__comment--user-post' : '';
}
}
~省略
動作するが良くないコード
methodsに定義すれば引数を取得でき、動きはするが再計算がされないので複数回の描画の際に積む。
(たとえば、クリックしたらそのコメントが強調されるようにしたいなどの要件に対応できない)
Comments.Vue
<template>
<div v-for="(comment, comments)" class="comments">
<p class="comments__comment" :class="isUserPostClass(comment)"/>
</div>
</temlate>
~省略
methods: {
isUserPostClass(comment) {
return comment.isUserPost ? 'comments__comment--user-post' : '';
}
}
~省略
解決策
ではどうすればいいのかと言うと、タイトルにもある通りv-forの中身だけをコンポーネント分割すればOKである。
今回はコメント一覧画面なので、コメント単体のコンポーネントを作成し、propsにv−forで回しているコメントのObject、および変更を検知させて再計算させたいプロパティをを与えてあげれば問題ない。
その後、コメントコンポーネントの中でcomputedを定義すれば、v-forの中身でcomputedを使うことができる。
動作するコード
Comments.Vue
<template>
<div v-for="(comment, comments)" class="comments">
<UserComment :comment="comment" :isUserPost="comment.isUserPost" />
</div>
</temlate>
UserComment.Vue
<template>
<div class="user-comment" :class="isUserPostClass">
~内容~
</div>
</temlate>
~省略
props: {
comment: {
type: Object,
default: null
},
isUserPost: {
type: Boolean,
default: false
}
},
computed: {
isUserPostClass() {
return this.isUserPost ? 'user-comment--user-post' : '';
}
}
~省略
まとめ
- v-forの中でcomputedを使いたくなったら、ループ対象であるDOMをコンポーネント分割しよう
- computedでプロパティの内容が変わったときの再計算処理を走らせるためには、明示的にpropsとして渡したほうが安全