これって、Qiita記事の種になりませんか?
つまり、こういうことになります
-
/layouts
にヘッダーのコンポーネントを含めつつ -
/pages
のファイルから/layouts
で使用しているコンポーネントの値を設定する(値を渡す)
基本構成
- レイアウト内でヘッダーコンポーネントを読み込み、表示している。
- 上記レイアウトを適用してpagesを表示。
-
layout(components) > pages
の親子関係
フォルダ
/components
- Header.vue
/layouts
- ore.vue
/pages
- index.vue
ファイル
components/Header.vue
<template>
<h1>{{ title }}</h1>
</template>
<script>
export default {
props: {
title: {
type: String,
default: ''
}
}
}
</script>
layouts/ore.vue
<template>
<div>
<Header />
<nuxt />
</div>
</template>
<script>
import Header from '@/components/Header'
export default {
components: {
Header
}
}
</script>
pages/index.vue
<template>
<div>
abc
</div>
</template>
<script>
export default {
name: 'sample',
layout: 'ore'
}
</script>
方法
1. page側から$emitでlayout側に値を渡す。
- dataに値を定義。
- methodsに$emitで渡すメソッドを定義。
- mountedのタイミングで2のメソッドを実行。
pages/index.vue
<template>
<div>
abc
</div>
</template>
<script>
export default {
name: 'sample',
layout: 'ore',
data(){
return {
header: {
title: 'ページタイトル'
}
}
},
mounted() {
this.updateHeader()
},
methods: {
updateHeader() {
// タイトルとして使いたい情報を渡す
this.$nuxt.$emit('updateHeader', this.header.title)
}
}
}
</script>
2. layout側でイベントを受け取るようにする
- dataにタイトルの初期値を定義。
-
createdのタイミングでイベントリスナーを設定。
$nuxt.on
で定義する。 - 要素に
@eventName
の形式で記述する形だとmounted以後にしか設定されない。 - そのため、mounted直後の子要素のイベント発火を検知できなかった。
- イベントを検知した後、タイトルを書き換える処理を追加。
layouts/ore.vue
<template>
<div>
<Header />
<nuxt />
</div>
</template>
<script>
import Header from '@/components/Header'
export default {
components: {
Header
},
data() {
return {
title: ''
}
},
created() {
this.setListener()
},
methods: {
setListener() {
// emitで発火させたイベント名にする
this.$nuxt.$on('updateHeader', this.setHeader)
},
setHeader(title) {
// 第1引数にはemitで渡した値が入ってくる。
// 第2引数以降を渡す場合も同様に、それ以降の引数で受け取れる
this.title = title || ''
}
}
}
</script>
3.受け取った値を反映させる
- レイアウトのHeaderコンポーネントに対し、
:propsの名前="dataで定義している値"
の記述を追加する
layouts/ore.vue
<template>
<div>
<Header :title="title" />
<nuxt />
</div>
</template>
...略
最終的なファイルの状態
※コンポーネントは最初と変化なし
components/Header.vue
<template>
<h1>{{ title }}</h1>
</template>
<script>
export default {
props: {
title: {
type: String,
default: ''
}
}
}
</script>
layouts/ore.vue
<template>
<div>
<Header :title="title" />
<nuxt />
</div>
</template>
<script>
import Header from '@/components/Header'
export default {
components: {
Header
},
data() {
return {
title: ''
}
},
created() {
this.setListener()
},
methods: {
setListener() {
this.$nuxt.$on('updateHeader', this.setHeader)
},
setHeader(title) {
this.title = title || ''
}
}
}
</script>
pages/index.vue
<template>
<div>
abc
</div>
</template>
<script>
export default {
name: 'sample',
layout: 'ore',
data(){
return {
header: {
title: 'ページタイトル'
}
}
},
mounted() {
this.updateHeader()
},
methods: {
updateHeader() {
this.$nuxt.$emit('updateHeader', this.header.title)
}
}
}
</script>
まとめ
-
$nuxt.$emit
でイベント発火+親に値を渡す。 -
$nuxt.$on('イベント名')
で、イベントと値を受け取る。 - イベントリスナーはcreatedのタイミングで付与しないとまともにイベントが受け取れない。
-
$nuxt
の存在を知らないと設定方法で詰む - propsの直接の書き換えは怒られるので、dataでやる。
さらにカスタマイズ
- $emitで複数渡す場合はlayout側のdataを増やし、コンポーネントのバインドも複数にすればOK。
- setListenerに複数のイベントリスナーを定義すれば、いろんな更新イベントを受け取れる。
- 今回はヘッダーしか更新しないから
updateHeader
だけど、複数のコンポーネントを更新するならupdateLayout
になりそう。- イベント発火はコンポーネント単位で分割したほうが良い
- 複数のイベント発火をする処理を
updateLayout
で実行するイメージ