diff --git a/README.md b/README.md
index 0d98ad1..e470be6 100644
--- a/README.md
+++ b/README.md
@@ -458,6 +458,30 @@ Each item in the array maps to a newly-created `` element, where object pr
```
+Since v1.5.0, you can now set up meta templates that work similar to the titleTemplate:
+
+```js
+{
+ metaInfo: {
+ meta: [
+ { charset: 'utf-8' },
+ {
+ 'vmid': 'og:title',
+ 'property': 'og:title',
+ 'content': 'Test title',
+ 'template': chunk => `${chunk} - My page` //or as string template: '%s - My page'
+ }
+ ]
+ }
+}
+```
+
+```html
+
+
+```
+
+
#### `link` ([Object])
Each item in the array maps to a newly-created `` element, where object properties map to attributes.
diff --git a/src/shared/constants.js b/src/shared/constants.js
index 7bd5c55..0165fe4 100644
--- a/src/shared/constants.js
+++ b/src/shared/constants.js
@@ -19,3 +19,6 @@ export const VUE_META_SERVER_RENDERED_ATTRIBUTE = 'data-vue-meta-server-rendered
// that both have `vmid` of "description", then vue-meta will overwrite the
// shallowest one with the deepest one.
export const VUE_META_TAG_LIST_ID_KEY_NAME = 'vmid'
+
+// This is the key name for possible meta templates
+export const VUE_META_TEMPLATE_KEY_NAME = 'template'
diff --git a/src/shared/getComponentOption.js b/src/shared/getComponentOption.js
index 37d3193..ae76e2b 100644
--- a/src/shared/getComponentOption.js
+++ b/src/shared/getComponentOption.js
@@ -15,7 +15,7 @@ import deepmerge from 'deepmerge'
* @return {Object} result - final aggregated result
*/
export default function getComponentOption (opts, result = {}) {
- const { component, option, deep, arrayMerge } = opts
+ const { component, option, deep, arrayMerge, metaTemplateKeyName } = opts
const { $options } = component
if (component._inactive) return result
@@ -48,6 +48,22 @@ export default function getComponentOption (opts, result = {}) {
}, result)
})
}
+ if (metaTemplateKeyName && result.hasOwnProperty('meta')) {
+ result.meta = Object.keys(result.meta).map(metaKey => {
+ const metaObject = result.meta[metaKey]
+ if (!metaObject.hasOwnProperty(metaTemplateKeyName) || !metaObject.hasOwnProperty('content') || typeof metaObject[metaTemplateKeyName] === 'undefined') {
+ return result.meta[metaKey]
+ }
+ const template = metaObject[metaTemplateKeyName]
+ delete metaObject[metaTemplateKeyName]
+
+ if (template) {
+ metaObject.content = typeof template === 'function' ? template(metaObject.content) : template.replace(/%s/g, metaObject.content)
+ }
+
+ return metaObject
+ })
+ }
return result
}
diff --git a/src/shared/getMetaInfo.js b/src/shared/getMetaInfo.js
index 8774487..c8203fe 100644
--- a/src/shared/getMetaInfo.js
+++ b/src/shared/getMetaInfo.js
@@ -20,7 +20,7 @@ const escapeHTML = (str) => typeof window === 'undefined'
.replace(/'/g, '\u0027')
export default function _getMetaInfo (options = {}) {
- const { keyName, tagIDKeyName } = options
+ const { keyName, tagIDKeyName, metaTemplateKeyName } = options
/**
* Returns the correct meta info for the given component
* (child components will overwrite parent meta info)
@@ -52,6 +52,7 @@ export default function _getMetaInfo (options = {}) {
component,
option: keyName,
deep: true,
+ metaTemplateKeyName,
arrayMerge (target, source) {
// we concat the arrays without merging objects contained in,
// but we check for a `vmid` property on each object in the array
diff --git a/src/shared/plugin.js b/src/shared/plugin.js
index 1649348..86f5caa 100644
--- a/src/shared/plugin.js
+++ b/src/shared/plugin.js
@@ -6,7 +6,8 @@ import {
VUE_META_KEY_NAME,
VUE_META_ATTRIBUTE,
VUE_META_SERVER_RENDERED_ATTRIBUTE,
- VUE_META_TAG_LIST_ID_KEY_NAME
+ VUE_META_TAG_LIST_ID_KEY_NAME,
+ VUE_META_TEMPLATE_KEY_NAME
} from './constants'
// automatic install
@@ -22,6 +23,7 @@ export default function VueMeta (Vue, options = {}) {
// set some default options
const defaultOptions = {
keyName: VUE_META_KEY_NAME,
+ metaTemplateKeyName: VUE_META_TEMPLATE_KEY_NAME,
attribute: VUE_META_ATTRIBUTE,
ssrAttribute: VUE_META_SERVER_RENDERED_ATTRIBUTE,
tagIDKeyName: VUE_META_TAG_LIST_ID_KEY_NAME
diff --git a/test/getMetaInfo.spec.js b/test/getMetaInfo.spec.js
index acf1c09..3f45aab 100644
--- a/test/getMetaInfo.spec.js
+++ b/test/getMetaInfo.spec.js
@@ -1,10 +1,11 @@
import Vue from 'vue'
import _getMetaInfo from '../src/shared/getMetaInfo'
import {
- VUE_META_KEY_NAME,
VUE_META_ATTRIBUTE,
+ VUE_META_KEY_NAME,
VUE_META_SERVER_RENDERED_ATTRIBUTE,
- VUE_META_TAG_LIST_ID_KEY_NAME
+ VUE_META_TAG_LIST_ID_KEY_NAME,
+ VUE_META_TEMPLATE_KEY_NAME
} from '../src/shared/constants'
// set some default options
@@ -12,6 +13,7 @@ const defaultOptions = {
keyName: VUE_META_KEY_NAME,
attribute: VUE_META_ATTRIBUTE,
ssrAttribute: VUE_META_SERVER_RENDERED_ATTRIBUTE,
+ metaTemplateKeyName: VUE_META_TEMPLATE_KEY_NAME,
tagIDKeyName: VUE_META_TAG_LIST_ID_KEY_NAME
}
@@ -108,7 +110,7 @@ describe('getMetaInfo', () => {
component = new Vue({
metaInfo: {
title: 'Hello',
- titleTemplate: titleTemplate,
+ titleTemplate,
meta: [
{ charset: 'utf-8' }
]
@@ -117,7 +119,7 @@ describe('getMetaInfo', () => {
expect(getMetaInfo(component)).to.eql({
title: 'Hello Function World',
titleChunk: 'Hello',
- titleTemplate: titleTemplate,
+ titleTemplate,
htmlAttrs: {},
headAttrs: {},
bodyAttrs: {},
@@ -142,7 +144,7 @@ describe('getMetaInfo', () => {
component = new Vue({
metaInfo: {
title: 'Hello',
- titleTemplate: titleTemplate,
+ titleTemplate,
meta: [
{ charset: 'utf-8' }
]
@@ -156,7 +158,7 @@ describe('getMetaInfo', () => {
expect(getMetaInfo(component)).to.eql({
title: 'Hello Function World',
titleChunk: 'Hello',
- titleTemplate: titleTemplate,
+ titleTemplate,
htmlAttrs: {},
headAttrs: {},
bodyAttrs: {},
@@ -172,4 +174,193 @@ describe('getMetaInfo', () => {
__dangerouslyDisableSanitizersByTagID: {}
})
})
+
+ it('properly uses string meta templates', () => {
+ component = new Vue({
+ metaInfo: {
+ title: 'Hello',
+ meta: [
+ {
+ vmid: 'og:title',
+ property: 'og:title',
+ content: 'Test title',
+ template: '%s - My page'
+ }
+ ]
+ }
+ })
+ expect(getMetaInfo(component)).to.eql({
+ title: 'Hello',
+ titleChunk: 'Hello',
+ titleTemplate: '%s',
+ htmlAttrs: {},
+ headAttrs: {},
+ bodyAttrs: {},
+ meta: [
+ {
+ vmid: 'og:title',
+ property: 'og:title',
+ content: 'Test title - My page'
+ }
+ ],
+ base: [],
+ link: [],
+ style: [],
+ script: [],
+ noscript: [],
+ __dangerouslyDisableSanitizers: [],
+ __dangerouslyDisableSanitizersByTagID: {}
+ })
+ })
+
+ it('properly uses function meta templates', () => {
+ component = new Vue({
+ metaInfo: {
+ title: 'Hello',
+ meta: [
+ {
+ vmid: 'og:title',
+ property: 'og:title',
+ content: 'Test title',
+ template: chunk => `${chunk} - My page`
+ }
+ ]
+ }
+ })
+ expect(getMetaInfo(component)).to.eql({
+ title: 'Hello',
+ titleChunk: 'Hello',
+ titleTemplate: '%s',
+ htmlAttrs: {},
+ headAttrs: {},
+ bodyAttrs: {},
+ meta: [
+ {
+ vmid: 'og:title',
+ property: 'og:title',
+ content: 'Test title - My page'
+ }
+ ],
+ base: [],
+ link: [],
+ style: [],
+ script: [],
+ noscript: [],
+ __dangerouslyDisableSanitizers: [],
+ __dangerouslyDisableSanitizersByTagID: {}
+ })
+ })
+
+ it('properly uses content only if template is not defined', () => {
+ component = new Vue({
+ metaInfo: {
+ title: 'Hello',
+ meta: [
+ {
+ vmid: 'og:title',
+ property: 'og:title',
+ content: 'Test title'
+ }
+ ]
+ }
+ })
+ expect(getMetaInfo(component)).to.eql({
+ title: 'Hello',
+ titleChunk: 'Hello',
+ titleTemplate: '%s',
+ htmlAttrs: {},
+ headAttrs: {},
+ bodyAttrs: {},
+ meta: [
+ {
+ vmid: 'og:title',
+ property: 'og:title',
+ content: 'Test title'
+ }
+ ],
+ base: [],
+ link: [],
+ style: [],
+ script: [],
+ noscript: [],
+ __dangerouslyDisableSanitizers: [],
+ __dangerouslyDisableSanitizersByTagID: {}
+ })
+ })
+
+ it('properly uses content only if template is null', () => {
+ component = new Vue({
+ metaInfo: {
+ title: 'Hello',
+ meta: [
+ {
+ vmid: 'og:title',
+ property: 'og:title',
+ content: 'Test title',
+ template: null
+ }
+ ]
+ }
+ })
+ expect(getMetaInfo(component)).to.eql({
+ title: 'Hello',
+ titleChunk: 'Hello',
+ titleTemplate: '%s',
+ htmlAttrs: {},
+ headAttrs: {},
+ bodyAttrs: {},
+ meta: [
+ {
+ vmid: 'og:title',
+ property: 'og:title',
+ content: 'Test title'
+ }
+ ],
+ base: [],
+ link: [],
+ style: [],
+ script: [],
+ noscript: [],
+ __dangerouslyDisableSanitizers: [],
+ __dangerouslyDisableSanitizersByTagID: {}
+ })
+ })
+
+ it('properly uses content only if template is false', () => {
+ component = new Vue({
+ metaInfo: {
+ title: 'Hello',
+ meta: [
+ {
+ vmid: 'og:title',
+ property: 'og:title',
+ content: 'Test title',
+ template: false
+ }
+ ]
+ }
+ })
+ expect(getMetaInfo(component)).to.eql({
+ title: 'Hello',
+ titleChunk: 'Hello',
+ titleTemplate: '%s',
+ htmlAttrs: {},
+ headAttrs: {},
+ bodyAttrs: {},
+ meta: [
+ {
+ vmid: 'og:title',
+ property: 'og:title',
+ content: 'Test title'
+ }
+ ],
+ base: [],
+ link: [],
+ style: [],
+ script: [],
+ noscript: [],
+ __dangerouslyDisableSanitizers: [],
+ __dangerouslyDisableSanitizersByTagID: {}
+ })
+ })
})