子の Component から発火されるイベントを親に知らせることは簡単だったけど、
親から子の Component に何か処理させる方法がわからなくて時間がかかった。
ファイル構成
src/
├ ViewModels/
│ ├ ChildA.svelte
│ └ ChildB.svelte
└ Views/
└ Parent.svelte
やりたいこと
- ChildA.svelte でイベント発火。(今回はクリックイベント)
- Parent.svelte で ChildA.svelte からのイベントを受取る。
- Parent.svelte から ChildB.svelte の中で定義した関数を呼び出す。
- Parent.svelte で ChildA.svelte からのイベントを受取る。
パターン 1 : 子A → 親
ChildA.svelte (コールバック)
- コールバックでイベント伝播させる。
- Event オブジェクトは受け取れない。
- 可読性は良い方だと思う。
<script>
export let didClick = () => {}
function onClick() {
didClick()
}
</script>
<style lang="scss"></style>
<button on:click={ onClick }>Fire</button>
Parent.svelte
- ChildA.svelte 内のクリックイベントを fire() で受け取る。
<script>
import ChildA.svelte from './../ViewModels/ChildA.svelte'
function fire() {
console.log('parent: fire')
}
</script>
<style lang="scss"></style>
<ChildA didClick={ fire }/>
パターン 2 : 子A → 親
ChildA.svelte (createEventDispatcher)
- createEventDispatcher でイベント伝播させる。
- Event オブジェクトを受け取れる。
- 可読性が悪い。
<script>
import { createEventDispatcher } from 'svelte'
const _dispatch = createEventDispatcher()
function click() {
_dispatch('didClick')
}
</script>
<style lang="scss"></style>
<button on:click={ click }>Fire</button>
Parent.svelte
- ChildA.svelte 内のクリックイベントを fire() で受け取る。
- dispatch を使う時は didClick={} → on:didClick={} になる
- Parent.svelte 側は on: がついてるので可読性は良いかもしれない。
<script>
import ChildA from './../ViewModels/ChildA.svelte'
function fire(e) {
console.log('parent: fire')
console.log(e)
}
</script>
<style lang="scss"></style>
<ChildA on:didClick={ fire }/>
親 → 子B
ChildB.svelte
- _countUp() を Parent.svelte から呼び出せるように準備をする。
- ここでも dispatch: ((name: string, detail?: any) => void) = createEventDispatcher() を使用する。
- CustomEvent オブジェクトの detail を使用してみる。
- detail に _methods() を渡す。
- 何かイベント発火させないと dispatch() は動かないので、 load イベントを使って発火させてみるが、ページのブラウザバックや単純にリンクから遷移してきた時に load イベントが発火しなかった。他にも pageshow や popstate を試してみたけど、どちらもうまくいかなかったので、 setTimeout() を使用するというひどい実装になった。
<script>
import { onMount } from 'svelte'
import { createEventDispatcher } from 'svelte'
const _dispatch = createEventDispatcher()
let counter = 0
function _methods() {
return {
countUp: () => {
_countUp()
}
}
}
function _countUp() {
counter += 1
}
onMount(() => {
_dispatch('didInit', _methods)
})
</script>
<style lang="scss"></style>
<div>{ counter }</div>
Parent.svelte
- ページ読み込み時に ChildB.svelte から didInit イベントを受け取る。
<script>
import ChildB.svelte from './../ViewModels/ChildB.svelte'
let childBEvent = null
function didInitChildB(e) {
childBEvent = e
}
</script>
<style lang="scss"></style>
<ChildB on:didInit={ didInitChildB }/>
子A → 親 → 子B
Parent.svelte
- ChildA.svelte はここではパターン 1 (コールバック) のほうで実装。
- ChildA.svelte と ChildB.svelte は上記参考。
- ChildA.svelte でクリックされたイベントを fire() で受け取り、ページ読み込み時に受け取った ChildB.svelte の CustomEvent オブジェクトの detail を使って countUp() を呼び出す。
<script>
import ChildA from './../ViewModels/ChildA.svelte'
import ChildB from './../ViewModels/ChildB.svelte'
let childBEvent = null
function didInitChildB(e) {
childBEvent = e
}
function fire() {
if (childBEvent) {
childBEvent.detail().countUp()
}
}
</script>
<style lang="scss"></style>
<ChildA didClick={ fire }/>
<ChildB on:didInit={ didInitChildB }/>