最近、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{}) => {
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」とつけるのは無駄なような気もするので、誰かもっといい方法を知っていたら教えてください。
コメント