From 7152ed42785585507c26c7df6deced7c6e16f590 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Mon, 12 Oct 2020 08:23:00 +0100 Subject: [PATCH] feat: allow computed properties within `useMeta` (#255) --- docs/content/en/useMeta.md | 6 +++- src/meta.ts | 71 +++++++++++++++---------------------- test/e2e/meta.ts | 6 ++++ test/fixture/pages/meta.vue | 14 +++++++- 4 files changed, 52 insertions(+), 45 deletions(-) diff --git a/docs/content/en/useMeta.md b/docs/content/en/useMeta.md index 9cc612e0..556eab03 100644 --- a/docs/content/en/useMeta.md +++ b/docs/content/en/useMeta.md @@ -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 @@ -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 })) }, }) ``` diff --git a/src/meta.ts b/src/meta.ts index 7de3e1e6..0def8f85 100644 --- a/src/meta.ts +++ b/src/meta.ts @@ -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' @@ -34,11 +33,10 @@ function assign>(target: T, source: Partial) { return target } -export function createEmptyMeta(): Omit< - MetaInfoMapper>, - 'titleTemplate' -> { +export function createEmptyMeta(): MetaInfoMapper> { return { + titleTemplate: (null as unknown) as undefined, + __dangerouslyDisableSanitizers: [], __dangerouslyDisableSanitizersByTagID: {}, @@ -67,11 +65,13 @@ export const getHeadOptions = (options: { options.head instanceof Function ? reactive({}) : reactive(options.head) + const _computedHead: Array> = [] 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> = { @@ -94,7 +94,7 @@ type ToRefs> = { ``` * @param init Whatever defaults you want to set for `head` properties. */ -export const useMeta = (init?: T) => { +export const useMeta = (init?: T | (() => T)) => { const vm = getCurrentInstance() if (!vm) throw new Error('useMeta must be called within a component.') @@ -103,38 +103,23 @@ export const useMeta = (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> + } assign(_head, createEmptyMeta()) - assign(_head, init || {}) - - const refs = toRefs(_head) as ToRefs< - ReturnType & { - titleTemplate: ReactiveHead['titleTemplate'] - } & T - > - - refs.titleTemplate = customRef( - (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(_head, init || {}) + } + + const refs = toRefs(_head) as ToRefs & T> + + if (process.client) { + watch(Object.values(refs), () => vm.$meta().refresh(), { immediate: true }) + } return refs } diff --git a/test/e2e/meta.ts b/test/e2e/meta.ts index 071efd11..8354d399 100644 --- a/test/e2e/meta.ts +++ b/test/e2e/meta.ts @@ -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') }) @@ -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') }) diff --git a/test/fixture/pages/meta.vue b/test/fixture/pages/meta.vue index f79cf43c..ee39b6b4 100644 --- a/test/fixture/pages/meta.vue +++ b/test/fixture/pages/meta.vue @@ -3,6 +3,7 @@

title-{{ title }} +

@@ -11,6 +12,7 @@ import { defineComponent, useMeta, + ref, computed, onMounted, } from '@nuxtjs/composition-api' @@ -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(() => { @@ -37,7 +49,7 @@ export default defineComponent({ titleTemplate.value = `%s - Changed` } - return { title, changeTitleTemplate, titleTemplate } + return { title, changeTitleTemplate, titleTemplate, setMessage } }, })