Skip to content

Commit

Permalink
feat: allow computed properties within useMeta (#255)
Browse files Browse the repository at this point in the history
  • Loading branch information
danielroe authored Oct 12, 2020
1 parent 7f056a6 commit 7152ed4
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 45 deletions.
6 changes: 5 additions & 1 deletion docs/content/en/useMeta.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ fullscreen: True
You can interact directly with [head properties](https://nuxtjs.org/api/pages-head/) in `setup` by means of the `useMeta()` helper.

```ts
import { defineComponent, useMeta, computed } from '@nuxtjs/composition-api'
import { defineComponent, useMeta, computed, ref } from '@nuxtjs/composition-api'

export default defineComponent({
// You need to define an empty head to activate this functionality
Expand All @@ -23,6 +23,10 @@ export default defineComponent({

// ... or simply set some meta tags
useMeta({ title: 'My page', ... })

// You can even pass a function to achieve a computed meta
const message = ref('')
useMeta(() => ({ title: message.value }))
},
})
```
71 changes: 28 additions & 43 deletions src/meta.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import defu from 'defu'
import Vue from 'vue'
import {
computed,
getCurrentInstance,
toRefs,
Ref,
reactive,
toRefs,
watch,
UnwrapRef,
customRef,
Ref,
set,
UnwrapRef,
} from '@vue/composition-api'

import type { MetaInfo } from 'vue-meta'
Expand All @@ -34,11 +33,10 @@ function assign<T extends Record<string, any>>(target: T, source: Partial<T>) {
return target
}

export function createEmptyMeta(): Omit<
MetaInfoMapper<Required<MetaInfo>>,
'titleTemplate'
> {
export function createEmptyMeta(): MetaInfoMapper<Required<MetaInfo>> {
return {
titleTemplate: (null as unknown) as undefined,

__dangerouslyDisableSanitizers: [],
__dangerouslyDisableSanitizersByTagID: {},

Expand Down Expand Up @@ -67,11 +65,13 @@ export const getHeadOptions = (options: {
options.head instanceof Function
? reactive<MetaInfo>({})
: reactive<MetaInfo>(options.head)
const _computedHead: Array<Ref<MetaInfo>> = []
const head =
options.head instanceof Function
? () => defu(_head, options.head())
: () => _head
return { _head, head }
? () =>
defu(..._computedHead.map(val => val.value), _head, options.head())
: () => defu(..._computedHead.map(val => val.value), _head, {})
return { _head, _computedHead, head }
}

type ToRefs<T extends Record<string, any>> = {
Expand All @@ -94,7 +94,7 @@ type ToRefs<T extends Record<string, any>> = {
```
* @param init Whatever defaults you want to set for `head` properties.
*/
export const useMeta = <T extends MetaInfo>(init?: T) => {
export const useMeta = <T extends MetaInfo>(init?: T | (() => T)) => {
const vm = getCurrentInstance()
if (!vm) throw new Error('useMeta must be called within a component.')

Expand All @@ -103,38 +103,23 @@ export const useMeta = <T extends MetaInfo>(init?: T) => {
'In order to enable `useMeta`, please make sure you include `head: {}` within your component definition, and you are using the `defineComponent` exported from @nuxtjs/composition-api.'
)

const { _head = reactive({}) as ReactiveHead } = vm.$options
const { _head, _computedHead } = vm.$options as {
_head: ReactiveHead
_computedHead: Array<Ref<MetaInfo>>
}

assign(_head, createEmptyMeta())
assign<MetaInfo>(_head, init || {})

const refs = toRefs(_head) as ToRefs<
ReturnType<typeof createEmptyMeta> & {
titleTemplate: ReactiveHead['titleTemplate']
} & T
>

refs.titleTemplate = customRef<ReactiveHead['titleTemplate']>(
(track, trigger) => {
return {
get() {
track()
return _head.titleTemplate
},
set(newValue) {
if (!_head.titleTemplate) {
set(_head, 'titleTemplate', newValue)
} else {
_head.titleTemplate = newValue
}
trigger()
},
}
}
)

if (process.client)
watch(Object.values(refs), vm.$meta().refresh, { immediate: true })
if (init instanceof Function) {
_computedHead.push(computed(init))
} else {
assign<MetaInfo>(_head, init || {})
}

const refs = toRefs(_head) as ToRefs<ReturnType<typeof createEmptyMeta> & T>

if (process.client) {
watch(Object.values(refs), () => vm.$meta().refresh(), { immediate: true })
}

return refs
}
6 changes: 6 additions & 0 deletions test/e2e/meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ test('Shows correct title on server-loaded page', async t => {
await t.click(Selector('button').withText('Change'))
await t.expect(Selector('title').innerText).eql('mounted title - Changed')

await t.click(Selector('button').withText('Set'))
await t.expect(Selector('meta').getAttribute('content')).eql('new message')

await t.click(Selector('a').withText('back'))
await t.expect(Selector('title').innerText).eql('My fixture - fixture')
})
Expand All @@ -30,4 +33,7 @@ test('Shows correct title on client-loaded page', async t => {

await t.click(Selector('button').withText('Change'))
await t.expect(Selector('title').innerText).eql('mounted title - Changed')

await t.click(Selector('button').withText('Set'))
await t.expect(Selector('meta').getAttribute('content')).eql('new message')
})
14 changes: 13 additions & 1 deletion test/fixture/pages/meta.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<p>
<code>title-{{ title }}</code>
<button @click="changeTitleTemplate">Change title template</button>
<button @click="setMessage">Set message</button>
</p>
</blockquote>
</template>
Expand All @@ -11,6 +12,7 @@
import {
defineComponent,
useMeta,
ref,
computed,
onMounted,
} from '@nuxtjs/composition-api'
Expand All @@ -23,6 +25,16 @@ export default defineComponent({
title.value = 'oldSetTitle'
const { title: newImport, bodyAttrs, titleTemplate } = useMeta()
const message = ref('')
const setMessage = () => (message.value = 'new message')
useMeta(() => ({
meta: [{
name: 'message',
content: message.value
}]
}))
newImport.value = 'newSetTitle'
onMounted(() => {
Expand All @@ -37,7 +49,7 @@ export default defineComponent({
titleTemplate.value = `%s - Changed`
}
return { title, changeTitleTemplate, titleTemplate }
return { title, changeTitleTemplate, titleTemplate, setMessage }
},
})
</script>

1 comment on commit 7152ed4

@vercel
Copy link

@vercel vercel bot commented on 7152ed4 Oct 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.