diff --git a/examples/vue-router/app.js b/examples/vue-router/app.js
index 56a43ea..6a3867c 100644
--- a/examples/vue-router/app.js
+++ b/examples/vue-router/app.js
@@ -5,7 +5,7 @@ import Router from 'vue-router'
Vue.use(Router)
Vue.use(VueMeta)
-const ChildComponent = () => ({
+const ChildComponent = {
name: `child-component`,
props: ['page'],
template: `
You're looking at the {{ page }} page
`,
@@ -14,13 +14,16 @@ const ChildComponent = () => ({
return this.page
}
}
-})
+}
+// this wrapper function is not a requirement for vue-router,
+// just a demonstration that render-function style components also work.
+// See https://github.com/declandewet/vue-meta/issues/9 for more info.
function view (page) {
return {
name: `section-${page}`,
render (h) {
- return h(ChildComponent(), {
+ return h(ChildComponent, {
props: { page }
})
}
diff --git a/src/server/inject.js b/src/server/inject.js
index 83b9263..bbbf108 100644
--- a/src/server/inject.js
+++ b/src/server/inject.js
@@ -9,20 +9,8 @@ import generateServerInjector from './generateServerInjector'
* @return {Object} - server meta info with `toString` methods
*/
export default function inject () {
- const Vue = this.constructor
-
// get meta info with sensible defaults
- const info = Vue.util.extend({
- title: '',
- htmlAttrs: {},
- bodyAttrs: {},
- meta: [],
- script: [],
- noscript: [],
- style: [],
- link: [],
- base: []
- }, getMetaInfo(this.$root))
+ const info = getMetaInfo(this.$root)
// generate server injectors
for (let key in info) {
diff --git a/src/shared/getComponentOption.js b/src/shared/getComponentOption.js
index a816082..7c751d8 100644
--- a/src/shared/getComponentOption.js
+++ b/src/shared/getComponentOption.js
@@ -23,12 +23,6 @@ export default function getComponentOption (opts, result = {}) {
const data = $options[option]
if (typeof data === 'object') {
- // bind context of option methods (if any) to this component
- Object.keys(data).forEach((key) => {
- const value = data[key]
- data[key] = typeof value === 'function' ? value.bind(component) : value
- })
-
// merge with existing options
result = deepmerge(result, data, {
clone: true,
diff --git a/src/shared/getMetaInfo.js b/src/shared/getMetaInfo.js
index 5b22298..a126eab 100644
--- a/src/shared/getMetaInfo.js
+++ b/src/shared/getMetaInfo.js
@@ -1,5 +1,6 @@
import deepmerge from 'deepmerge'
import getComponentOption from './getComponentOption'
+import mergeComponentData from './mergeComponentData'
/**
* Returns the correct meta info for the given component
@@ -55,12 +56,6 @@ export default function getMetaInfo (component) {
}
})
- // if any info options are a function, coerce them to the result of a call
- Object.keys(info).forEach((key) => {
- const val = info[key]
- info[key] = typeof val === 'function' && key !== 'changed' ? val() : val
- })
-
// backup the title chunk in case user wants access to it
if (info.title) {
info.titleChunk = info.title
@@ -77,5 +72,16 @@ export default function getMetaInfo (component) {
info.base = Object.keys(info.base).length ? [info.base] : []
}
- return deepmerge(defaultInfo, info)
+ const metaInfo = deepmerge(defaultInfo, info)
+ const componentData = mergeComponentData(component)
+
+ // inject component context into functions & call to normalize data
+ Object.keys(metaInfo).forEach((key) => {
+ const val = metaInfo[key]
+ if (typeof val === 'function') {
+ metaInfo[key] = val.call(componentData)
+ }
+ })
+
+ return metaInfo
}
diff --git a/src/shared/mergeComponentData.js b/src/shared/mergeComponentData.js
new file mode 100644
index 0000000..648fd97
--- /dev/null
+++ b/src/shared/mergeComponentData.js
@@ -0,0 +1,16 @@
+/**
+ * Recursively shallow-merges component object with it's children component objects.
+ * This function is responsible for obtaining the `this` context of metaInfo props when
+ * declared in function form.
+ *
+ * @param {Object} component - the component object
+ * @return {Object} - the merged data
+ */
+export default function mergeComponentData (component) {
+ if (component.$children.length) {
+ return component.$children.reduce((data, child) => {
+ return Object.assign({}, data, mergeComponentData(child))
+ }, component)
+ }
+ return component
+}
diff --git a/src/shared/plugin.js b/src/shared/plugin.js
index 4aeeefe..05630a2 100644
--- a/src/shared/plugin.js
+++ b/src/shared/plugin.js
@@ -26,9 +26,10 @@ export default function VueMeta (Vue) {
requestId = window.requestAnimationFrame(() => {
requestId = null
+ const info = getMetaInfo(this.$root)
// update the meta info
- updateClientMetaInfo(getMetaInfo(this.$root))
+ updateClientMetaInfo(info)
})
}
})
diff --git a/test/getComponentOption.spec.js b/test/getComponentOption.spec.js
index 8a7d5a6..93b548c 100644
--- a/test/getComponentOption.spec.js
+++ b/test/getComponentOption.spec.js
@@ -19,21 +19,6 @@ describe('getComponentOption', () => {
expect(fetchedOption).to.eql('foo')
})
- it('binds option method context to the component instance', () => {
- component = new Vue({
- data: {
- age: 44
- },
- foo: {
- bar () {
- return this.age
- }
- }
- })
- const fetchedOption = getComponentOption({ component, option: 'foo' })
- expect(fetchedOption.bar()).to.equal(44)
- })
-
it('fetches deeply nested component options and merges them', () => {
Vue.component('merge-child', { template: '', foo: { bar: 'baz' } })