Custom Events
Con báo ngược lên cha bằng cách emit event. Cha lắng nghe bằng v-on/@. Đây là chiều còn lại của luồng dữ liệu một chiều (cha xuống con bằng props, con lên cha bằng events).
Emit và lắng nghe
Con phát event bằng this.$emit('event-name', payload).
<!-- Child: ToggleButton.vue -->
<template>
<button @click="$emit('toggle')">Toggle</button>
</template>
<!-- Parent -->
<toggle-button @toggle="onToggle"></toggle-button>
Truyền dữ liệu kèm event
Tham số sau tên event là payload, cha nhận trong handler.
<template>
<button @click="$emit('select', item.id)">Chọn</button>
</template>
<item-row @select="onSelect"></item-row>
methods: {
onSelect(id) {
console.log('selected', id)
}
}
Cần nhiều giá trị thì emit một object.
this.$emit('change', { field: 'name', value: 'Nhan' })
Tên event phải kebab-case
Khác với props/component, tên event không tự chuyển đổi case. Vue khuyến nghị luôn dùng kebab-case cho tên event, vì HTML không phân biệt hoa thường.
// emit
this.$emit('item-selected')
<!-- lắng nghe đúng tên -->
<my-item @item-selected="..."></my-item>
v-model trên component
v-model trên component là cú pháp rút gọn của: bind prop value cộng lắng nghe event input.
<custom-input v-model="searchText"></custom-input>
<!-- tương đương -->
<custom-input
:value="searchText"
@input="searchText = $event">
</custom-input>
Component phải khai báo prop value và emit input:
<template>
<input :value="value" @input="$emit('input', $event.target.value)">
</template>
<script>
export default {
props: { value: String }
}
</script>
Đổi tên prop/event mặc định bằng option model:
export default {
model: {
prop: 'checked',
event: 'change'
},
props: { checked: Boolean }
}
.sync modifier
.sync cho phép two-way binding một prop bất kỳ. Cha viết :prop.sync="x", con emit update:prop.
<modal-box :visible.sync="showModal"></modal-box>
// trong con: đóng modal
this.$emit('update:visible', false)
Giao tiếp ngoài quan hệ cha-con
$emit/props chỉ giải quyết cha-con trực tiếp. Component không liên quan trực tiếp thì dùng event bus đơn giản (Vue 2) hoặc state store. Với app nhỏ trong AEM thường không cần; nâng state lên component cha chung là đủ.
// event bus tối giản (Vue 2.7)
const bus = new Vue()
bus.$emit('refresh')
bus.$on('refresh', handler)
Tips cho AEM
Trong một AEM component mount Vue, child component emit event lên root để root xử lý gọi API hoặc cập nhật state chung.
<!-- root: SearchWidget.vue -->
<template>
<div>
<search-input @search="fetchResults" />
<result-list :items="results" />
</div>
</template>
<script>
import SearchInput from './SearchInput.vue'
import ResultList from './ResultList.vue'
export default {
components: { SearchInput, ResultList },
data() {
return { results: [] }
},
methods: {
async fetchResults(keyword) {
const res = await fetch(`/bin/search?q=${encodeURIComponent(keyword)}`)
this.results = await res.json()
}
}
}
</script>