最近、Vue.jsはComposition API(<script setup>)かつTypeScriptで書くようにしています。
ところで昔から、Vue.jsを使っていると、v-forで利用している要素をメソッドではなくcomputedで計算したいなんて思う場面がたまにあります。そういう場合、v-forを使ってるタグを子コンポーネントにして、配列要素を属性で渡すようにして子コンポーネント内でcomputedを使うなんてことをやっていることもあったのですが、VuetifyなどUIフレームワークを使っているとそれが難しいこともあります(テーブル内のtrをv-forでループしたい場合、trタグだけで子コンポーネントにするのもなんだか微妙な気もしますし)。
ただ、Vue3のComposition APIの場合、コンポーザブル関数でcomputed変数を返すようにし、それを配列に含めるようにしたらいいのではないかと思い、やってみました。
<script lang="ts"> import { ref, reactive, computed, toRefs } from "vue"; export interface TypeScore { name: string; japanese: number; english: number; mathmatic: number; } // スコア用のコンポーザブル関数 export const useScore = (score: Partial<TypeScore> = {}) => { const scoreObj = reactive<TypeScore>({ name: "", japanese: 0, english: 0, mathmatic: 0, }); Object.assign(scoreObj, score); const totalScore = computed( () => scoreObj.japanese + scoreObj.english + scoreObj.mathmatic ); return { ...toRefs(scoreObj), totalScore, }; }; </script> <script setup lang="ts"> // ReturnTypeで関数の戻り値の型を取得する const scores = ref<ReturnType<typeof useScore>[]>([]); scores.value.push( // reaactive()を指定しないと型エラーになる(実際は、無くても動くよう) reactive( useScore({ name: "田中", japanese: 60, english: 70, mathmatic: 40, }) ), reactive( useScore({ name: "佐藤", japanese: 50, english: 50, mathmatic: 80, }) ) ); const name = ref(""); const addScore = () => { if (!name.value) return; scores.value.push( reactive( useScore({ name: name.value, }) ) ); name.value = ""; }; </script>
<template> <h1>v-forの中でcomputedを使う</h1> <div> 名前:<input type="text" v-model="name" @keydown.enter="addScore" /><input type="button" value="追加" @click="addScore" /> </div> <table class="score_table"> <thead> <tr> <th>名前</th> <th>国語</th> <th>英語</th> <th>数学</th> <th>合計</th> </tr> </thead> <tbody> <tr v-for="score in scores" :key="score.name"> <td>{{ score.name }}</td> <td> <input type="number" v-model="score.japanese" min="0" max="100" /> </td> <td> <input type="number" v-model="score.english" min="0" max="100" /> </td> <td> <input type="number" v-model="score.mathmatic" min="0" max="100" /> </td> <td>{{ score.totalScore }}</td> </tr> </tbody> </table> </template>
サンプル:v-forの中でcomputedを使う
国語、英語、数学の点数にたいして、computedでその合計点数を表示するようなサンプルです。
ようは、配列変数に、リアクティブ変数とcomputed変数をまとめたオブジェクトをいれているわけです。こうすることで、v-forで使えます。
ただ、よく分からないのが、TypeScriptを使っていると配列変数に入れる際に、「reactive(useScore({}))」というようにreactiveをつけないといけないということ。これを付けないと型エラーで怒られるようです。
でも、どうやら書かなくても動きはするようなんですよね…。わざわざ「reactive」とつけるのは無駄なような気もするので、誰かもっといい方法を知っていたら教えてください。
コメント