Edited at

v-for でリスト要素 (Object[]) の状態が変化しても反映されない現象

親から props 経由で渡される Array<{ item: string, value: string|number, checked: boolean }> みたいな物を v-for で回してチェックボックスを描画した際に、 checked の状態が変化しても HTML 要素として意図通りに描画されなくて困ったので、解決方法をメモします。


状況

v-for を使ってチェックボックスのリストを描画している時に、

取り回している配列内の Object が変化しても、担当のチェックボックスの見た目上の状態が変化しなかった。

コードはざっくり以下のような感じ。


some.vue

<dl>

<dt> hogehoge </dt>
<dd
v-for="item of list"
:key="`item-${item.value}`"
>
<input
:id="genDdId(item.value)"
:checked="item.checked"
:name="item.value"
type="checkbox"
@click.prevent="handleItemClick($event, item.value)"
>
<label :for="genDdId(item.value)">
{{ item.label }}
</label>
</dd>
</dl>

:checked="item.checked" の真偽値が変化する度にチェックボックスの checked 属性が変化して欲しいけど、してくれない。

$forceUpdate を試したけど変化無し。


原因と解決方法


原因

配列の変化は見てるけど、その各アイテムであるオブジェクトのプロパティまでは監視していないよ、という事だと推測。

普通に Vue.js の仕様に引っ掛かってしまっただけかも。

v-for で繰り返し表示させる際は key が必要になるわけだけど、この key はリスト内の位置までしか担当してくれてないのかな。


Vue が各ノードの識別情報を追跡できるヒントを与えるために、また、先ほど説明したような既存の要素の再利用と並び替えができるように、一意な key 属性を全てのアイテムに与える必要があります。

(https://jp.vuejs.org/v2/guide/list.html#key)



解決方法

key が違うなら異なる要素なので描画されるよね、と考えて、

状態により key が常に変化するように仕組んで解決しました。

以下のイメージ。


some.vue

<dd

v-for="item of list"
:key="`item-${item.value}--checked-${item.checked ? 1 : 0}`"
>