diff --git a/README.md b/README.md index a2030cc..831bcb1 100644 --- a/README.md +++ b/README.md @@ -322,6 +322,31 @@ new Vue({ } } ``` +## Manually updated + +Sometimes, we should update properties manually to sync data from server side. +Using `$updateAsyncComputed(key)` to update manually and get promise for updating. + +For example: +```js +new Vue({ + asyncComputed: { + articleList() { + return Vue.http.get('/article/list') + } + }, + methods: { + newArticle() { + Vue.http.post('/article/new', { + title: 'New Article', + content: 'Hello world' + }) + .then(() => this.$updateAsyncComputed('articleList')) + .then(() => console.log('done')) + } + } +}) +``` ## Error handling diff --git a/src/index.js b/src/index.js index 13d5141..e22dbc2 100644 --- a/src/index.js +++ b/src/index.js @@ -18,6 +18,8 @@ const AsyncComputed = { .optionMergeStrategies .asyncComputed = Vue.config.optionMergeStrategies.computed + Vue.prototype.$updateAsyncComputed = $updateAsyncComputed + Vue.mixin({ beforeCreate () { const optionData = this.$options.data @@ -28,6 +30,8 @@ const AsyncComputed = { this.$options.computed[prefix + key] = getterFn(key, this.$options.asyncComputed[key]) } + this._asyncComputedWatcher = {} + this.$options.data = function vueAsyncComputedInjectedDataFn () { const data = ( (typeof optionData === 'function') @@ -59,7 +63,8 @@ const AsyncComputed = { for (const key in this.$options.asyncComputed || {}) { let promiseId = 0 - this.$watch(prefix + key, newPromise => { + + const watcher = newPromise => { const thisPromise = ++promiseId if (newPromise === DidNotUpdate) { @@ -88,7 +93,10 @@ const AsyncComputed = { handler(err.stack) } }) - }, { immediate: true }) + } + + this.$watch(prefix + key, watcher, {immediate: true}) + this._asyncComputedWatcher[key] = watcher } } }) @@ -147,6 +155,21 @@ function generateDefault (fn, pluginOptions) { } } +function $updateAsyncComputed (key) { + const prefixedKey = prefix + key + if (this.hasOwnProperty(key) && this.hasOwnProperty(prefixedKey) && this._asyncComputedWatcher[key]) { + this._computedWatchers[prefixedKey].evaluate() + const watcher = this._asyncComputedWatcher[key] + const newPromise = this[prefixedKey] + return newPromise.then(() => { + watcher.apply(this, [newPromise]) + return newPromise + }) + } else { + throw new Error('Can not find async computed property ' + JSON.stringify(key)) + } +} + export default AsyncComputed /* istanbul ignore if */ diff --git a/test/index.js b/test/index.js index cd9e578..9e3fdd3 100644 --- a/test/index.js +++ b/test/index.js @@ -716,3 +716,42 @@ test("shouldUpdate works with lazy", t => { }) }) }) + +test('$updateAsyncComputed works', t => { + t.plan(7) + + let countA = 1 + let countB = 1 + + const vm = new Vue({ + asyncComputed: { + async_a () { return new Promise(resolve => setTimeout(() => resolve(countA++), 6)) }, + lazy_async_b: { + lazy: true, + get () { return new Promise(resolve => setTimeout(() => resolve(countB++), 6)) }, + } + } + }) + + t.equal(vm.async_a, null) + setTimeout(() => { + t.equal(vm.async_a, 1) + // eslint-disable-next-line promise/catch-or-return + vm.$updateAsyncComputed('async_a').then(() => t.equal(vm.async_a, 2)) + }, 12) + + setTimeout(() => { + t.equal(vm.lazy_async_b, null) + setTimeout(() => { + t.equal(vm.lazy_async_b, 1) + // eslint-disable-next-line promise/catch-or-return + vm.$updateAsyncComputed('lazy_async_b').then(() => t.equal(vm.lazy_async_b, 2)) + }, 12) + }, 12) + + try { + vm.$updateAsyncComputed('wrong_property') + } catch (err) { + t.equal(err.message, 'Can not find async computed property ' + JSON.stringify('wrong_property')) + } +})