mirror of
https://github.com/tenrok/vue-meta.git
synced 2026-05-30 14:24:04 +03:00
Merge pull request #18 from declandewet/watcher-logic
circumvent need to call refresh() after async actions
This commit is contained in:
@@ -681,38 +681,28 @@ Easy. Instead of defining `metaInfo` as an object, define it as a function and a
|
||||
|
||||
## How do I populate `metaInfo` from the result of an asynchronous action?
|
||||
|
||||
`vue-meta` exposes a method called `refresh` on the client-side that allows you to trigger an update at any given point in time.
|
||||
`vue-meta` will do this for you automatically when your component state changes.
|
||||
|
||||
In the same way you access `$meta().inject()` on the server, you can access `$meta().refresh()`.
|
||||
|
||||
For example, if you're using Vuex and you have an action that fetches a `post` asynchronously, you should ensure that it returns a promise so that you are notified when the fetching is complete:
|
||||
Just make sure that you're using the function form of `metaInfo`:
|
||||
|
||||
```js
|
||||
{
|
||||
actions: {
|
||||
async fetchPost ({ commit }, payload) {
|
||||
const post = yield db.fetch('posts', payload.postId)
|
||||
commit('fetchedPost', post)
|
||||
data () {
|
||||
return {
|
||||
title: 'Foo Bar Baz'
|
||||
}
|
||||
}
|
||||
metaInfo () {
|
||||
return {
|
||||
title: this.title
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then in your component, you can call `refresh()` to trigger an update once the fetch is complete:
|
||||
Check out the [vuex-async](https://github.com/declandewet/vue-meta/tree/master/examples/vuex-async) example for a far more detailed demonstration if you have doubts.
|
||||
|
||||
```js
|
||||
{
|
||||
beforeMount () {
|
||||
const postId = this.$router.params.id
|
||||
this.$store.dispatch('fetchPost', { postId })
|
||||
.then(() => this.$meta().refresh())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Just make sure that whatever data source you're using (`store` if you're using Vuex, component `data` otherwise) has some sane defaults set so Vue doesn't complain about `null` property accessors.
|
||||
|
||||
Check out the [vuex-async](https://github.com/declandewet/vue-meta/tree/master/examples/vuex-async) example for a far more detailed demonstration of how this works.
|
||||
Credit & Thanks for this feature goes to [Sébastien Chopin](https://github.com/Atinux).
|
||||
|
||||
# Examples
|
||||
|
||||
|
||||
@@ -63,15 +63,10 @@ export default new Vuex.Store({
|
||||
actions: {
|
||||
getPost ({ commit }, payload) {
|
||||
commit('loadingState', { isLoading: true })
|
||||
// we have to return a promise from this action so we know
|
||||
// when it is finished
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
commit('getPost', payload)
|
||||
resolve()
|
||||
}, 2000)
|
||||
})
|
||||
.then(() => commit('loadingState', { isLoading: false }))
|
||||
setTimeout(() => {
|
||||
commit('getPost', payload)
|
||||
commit('loadingState', { isLoading: false })
|
||||
}, 2000)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -18,11 +18,7 @@
|
||||
name: 'post',
|
||||
beforeMount () {
|
||||
const { slug } = this.$route.params
|
||||
// since fetching a post is asynchronous,
|
||||
// we need to call `this.$meta().refresh()`
|
||||
// to update the meta info
|
||||
this.$store.dispatch('getPost', { slug })
|
||||
.then(() => this.$meta().refresh())
|
||||
},
|
||||
computed: mapGetters([
|
||||
'isLoading',
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Performs a batched update. Uses requestAnimationFrame to prevent
|
||||
* calling a function too many times in quick succession.
|
||||
* You need to pass it an ID (which can initially be `null`),
|
||||
* but be sure to overwrite that ID with the return value of batchUpdate.
|
||||
*
|
||||
* @param {(null|Number)} id - the ID of this update
|
||||
* @param {Function} callback - the update to perform
|
||||
* @return {Number} id - a new ID
|
||||
*/
|
||||
export default function batchUpdate (id, callback) {
|
||||
window.cancelAnimationFrame(id)
|
||||
return window.requestAnimationFrame(() => {
|
||||
id = null
|
||||
callback()
|
||||
})
|
||||
}
|
||||
+21
-7
@@ -1,5 +1,6 @@
|
||||
import assign from 'object-assign'
|
||||
import $meta from './$meta'
|
||||
import batchUpdate from '../client/batchUpdate'
|
||||
|
||||
import {
|
||||
VUE_META_KEY_NAME,
|
||||
@@ -33,18 +34,31 @@ export default function VueMeta (Vue, options = {}) {
|
||||
Vue.prototype.$meta = $meta(options)
|
||||
|
||||
// store an id to keep track of DOM updates
|
||||
let requestId = null
|
||||
let batchID = null
|
||||
|
||||
// watch for client side component updates
|
||||
Vue.mixin({
|
||||
beforeCreate () {
|
||||
// coerce function-style metaInfo to a computed prop so we can observe
|
||||
// it on creation
|
||||
if (typeof this.$options[options.keyName] === 'function') {
|
||||
this.$options.computed.$metaInfo = this.$options[options.keyName]
|
||||
}
|
||||
},
|
||||
created () {
|
||||
// if computed $metaInfo exists, watch it for updates & trigger a refresh
|
||||
// when it changes (i.e. automatically handle async actions that affect metaInfo)
|
||||
// credit for this suggestion goes to [Sébastien Chopin](https://github.com/Atinux)
|
||||
if (this.$metaInfo) {
|
||||
this.$watch('$metaInfo', () => {
|
||||
// batch potential DOM updates to prevent extraneous re-rendering
|
||||
batchID = batchUpdate(batchID, () => this.$meta().refresh())
|
||||
})
|
||||
}
|
||||
},
|
||||
beforeMount () {
|
||||
// batch potential DOM updates to prevent extraneous re-rendering
|
||||
window.cancelAnimationFrame(requestId)
|
||||
|
||||
requestId = window.requestAnimationFrame(() => {
|
||||
requestId = null
|
||||
this.$meta().refresh()
|
||||
})
|
||||
batchID = batchUpdate(batchID, () => this.$meta().refresh())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user