Appearance
Props
親コンポーネントから子コンポーネントへデータを渡す方法を解説します。
親から子へデータを渡すときはProps
を使用します。
親からバインディングによって、子のコンポーネントにデータを渡します。
基本的に親から子へ渡した変数は、子コンポーネント内ではreadOnly
になります。
子コンポーネントは、それぞれ独立しており一方のリアクティブが変更されても他のリアクティブは変更されないことが確認できます。
DANGER
子コンポーネントのリアクティブ変数を直接 props に変えると、コンソールにエラーが表示されます。
WARNING
親コンポーネントの変数を変更できないので、タスクチェックしても完了タスクの合計は増えません。
完了タスク : 0 / 2
Buy Apple.
Go Gym.
vue
<template>
<div>
<p>完了タスク : {{ countCompleted }} / {{ totalTask }}</p>
</div>
<ul>
<li
v-for="(item, index) in items"
:key="index"
>
<PropsOneItem
:id="item.id"
:name="item.name"
:completed="item.completed"
/>
</li>
</ul>
<div>
</div>
</template>
vue
<script setup lang="ts">
import { computed, ref } from 'vue';
import PropsOneItem from './PropsOneItem.vue';
interface Items {
id: number;
name: string;
completed: boolean;
}
const items = ref<Items[]>([
{ id: 1, name: 'Buy Apple.', completed: false},
{ id: 2, name: 'Go Gym.', completed: false},
]);
const countCompleted = computed(() => {
return items.value.reduce((acc, value) => acc + Number(value.completed), 0);
})
const totalTask = computed(() => {
return items.value.length;
})
</script>
vue
<template>
<div class="item">
<input
type="checkbox"
:value="itemCompleted"
>
<p class="task-name">
{{ name }}
</p>
</div>
</template>
vue
<script setup lang="ts">
import { ref } from 'vue';
interface Items {
id: number;
name: string;
completed: boolean;
}
const props = defineProps<Items>();
const itemCompleted = ref(props.completed);
</script>
Emits
上のタスク管理は、タスク全体の進捗を把握するには不十分です。
タスクをチェックしたら、タスク進捗にも反映したいですね。
子コンポーネントでの変更を親コンポーネントに反映する(教える)仕組みが必要になります。
ここでEmits
を使用します。
親のイベントハンドラーを、子コンポーネントの emit で受け取ります。@change-check-box="item.completed = !item.completed"
子はイベントハンドラーonCheckBox
の中で、emit("changeCheckBox")
に着火しています。
INFO
タスクチェックすると、完了タスクの合計に反映されます。
完了タスク : 0 / 2
Buy Apple.
Go Gym.
vue
<template>
<div>
<p>完了タスク : {{ countCompleted }} / {{ totalTask }}</p>
</div>
<ul>
<li
v-for="(item, index) in items"
:key="index"
>
<EmitsOneItem
:id="item.id"
:name="item.name"
:completed="item.completed"
@change-check-box="item.completed = !item.completed"
/>
</li>
</ul>
<div>
</div>
</template>
vue
<script setup lang="ts">
import { computed, ref } from 'vue';
import EmitsOneItem from './EmitsOneItem.vue';
interface Items {
id: number;
name: string;
completed: boolean;
}
const items = ref<Items[]>([
{ id: 1, name: 'Buy Apple.', completed: false},
{ id: 2, name: 'Go Gym.', completed: false},
]);
const countCompleted = computed(() => {
return items.value.reduce((acc, value) => acc + Number(value.completed), 0);
})
const totalTask = computed(() => {
return items.value.length;
})
</script>
vue
<template>
<div class="item">
<input
type="checkbox"
:value="itemCompleted"
@change="onCheckBox"
>
<p class="task-name">
{{ name }}
</p>
</div>
</template>
vue
<script setup lang="ts">
import { ref } from 'vue';
interface Items {
id: number;
name: string;
completed: boolean;
}
interface Emits {
(event: "changeCheckBox"): void;
}
const props = defineProps<Items>();
const emit = defineEmits<Emits>();
const itemCompleted = ref(props.completed);
const onCheckBox = () => {
emit("changeCheckBox");
}
</script>
v-model
Props と Emits の基本を抑えられたと思います。
Props と Emits で親子コンポーネント間の変更をリアクティブにしましたが、 v-model
を使用することで、より簡潔にリアクティブにすることが出来ます。
v-model を使ったコンポーネント間の内部の動きを確認することが出来ます。
完了タスク : 0 / 2
Buy Apple.
Go Gym.
vue
<template>
<div>
<p>完了タスク : {{ countCompleted }} / {{ totalTask }}</p>
</div>
<ul>
<li
v-for="(item, index) in items"
:key="index"
>
<VModelOneItem
:id="item.id"
:name="item.name"
v-model:completed="item.completed"
/>
</li>
</ul>
<div>
</div>
</template>
vue
<script setup lang="ts">
import { computed, ref } from 'vue';
import VModelOneItem from './VModelOneItem.vue';
interface Items {
id: number;
name: string;
completed: boolean;
}
const items = ref<Items[]>([
{ id: 1, name: 'Buy Apple.', completed: false},
{ id: 2, name: 'Go Gym.', completed: false},
]);
const countCompleted = computed(() => {
return items.value.reduce((acc, value) => acc + Number(value.completed), 0);
})
const totalTask = computed(() => {
return items.value.length;
})
</script>
vue
<template>
<div class="item">
<input
type="checkbox"
:value="completed"
@change="onCheckBox"
>
<p class="task-name">
{{ name }}
</p>
</div>
</template>
vue
<script setup lang="ts">
interface Items {
id: number;
name: string;
completed: boolean;
}
interface Emits {
(event: "update:completed", completed: boolean): void;
}
const props = defineProps<Items>();
const emit = defineEmits<Emits>();
const onCheckBox = (event) => {
const element = event.target as HTMLInputElement;
emit("update:completed", event.target.checked);
}
</script>