vue-async-computed
Advanced tools
Comparing version 3.4.1 to 3.5.0
@@ -5,2 +5,3 @@ <!-- START doctoc generated TOC please keep comment here to allow auto update --> | ||
- [v3.5.0](#v350) | ||
- [v3.4.0](#v340) | ||
@@ -25,2 +26,6 @@ - [v3.3.0](#v330) | ||
### v3.5.0 | ||
* [#45](https://github.com/foxbenjaminfox/vue-async-computed/pull/45): add a status property `$asyncComputed` to each Vue instance with information about the status | ||
of its async computed properties. | ||
### v3.4.0 | ||
@@ -27,0 +32,0 @@ * Add a `shouldUpdate` option, which can control when and if |
@@ -56,7 +56,12 @@ (function (global, factory) { | ||
const optionData = this.$options.data; | ||
const asyncComputed = this.$options.asyncComputed || {}; | ||
this.$asyncComputed = {}; | ||
if (!Object.keys(asyncComputed).length) return | ||
if (!this.$options.computed) this.$options.computed = {}; | ||
for (const key in this.$options.asyncComputed || {}) { | ||
this.$options.computed[prefix + key] = getterFn(key, this.$options.asyncComputed[key]); | ||
for (const key in asyncComputed) { | ||
const getter = getterFn(key, this.$options.asyncComputed[key]); | ||
this.$options.computed[prefix + key] = getter; | ||
} | ||
@@ -70,3 +75,3 @@ | ||
) || {}; | ||
for (const key in this.$options.asyncComputed || {}) { | ||
for (const key in asyncComputed) { | ||
const item = this.$options.asyncComputed[key]; | ||
@@ -96,3 +101,3 @@ if (isComputedLazy(item)) { | ||
let promiseId = 0; | ||
this.$watch(prefix + key, newPromise => { | ||
const watcher = newPromise => { | ||
const thisPromise = ++promiseId; | ||
@@ -107,5 +112,7 @@ | ||
} | ||
setAsyncState(this.$asyncComputed[key], 'updating'); | ||
newPromise.then(value => { | ||
if (thisPromise !== promiseId) return | ||
setAsyncState(this.$asyncComputed[key], 'success'); | ||
this[key] = value; | ||
@@ -115,2 +122,4 @@ }).catch(err => { | ||
setAsyncState(this.$asyncComputed[key], 'error'); | ||
this.$asyncComputed[key].exception = err; | ||
if (pluginOptions.errorHandler === false) return | ||
@@ -128,3 +137,11 @@ | ||
}); | ||
}, { immediate: true }); | ||
}; | ||
this.$asyncComputed[key] = { | ||
exception: null, | ||
update: () => { | ||
watcher(getterOnly(this.$options.asyncComputed[key])()); | ||
} | ||
}; | ||
setAsyncState(this.$asyncComputed[key], 'updating'); | ||
this.$watch(prefix + key, watcher, { immediate: true }); | ||
} | ||
@@ -136,2 +153,15 @@ } | ||
function setAsyncState (stateObject, state) { | ||
stateObject.state = state; | ||
stateObject.updating = state === 'updating'; | ||
stateObject.error = state === 'error'; | ||
stateObject.success = state === 'success'; | ||
} | ||
function getterOnly (fn) { | ||
if (typeof fn === 'function') return fn | ||
return fn.get | ||
} | ||
function getterFn (key, fn) { | ||
@@ -138,0 +168,0 @@ if (typeof fn === 'function') return fn |
@@ -55,7 +55,12 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; | ||
var optionData = this.$options.data; | ||
var asyncComputed = this.$options.asyncComputed || {}; | ||
this.$asyncComputed = {}; | ||
if (!Object.keys(asyncComputed).length) return; | ||
if (!this.$options.computed) this.$options.computed = {}; | ||
for (var key in this.$options.asyncComputed || {}) { | ||
this.$options.computed[prefix + key] = getterFn(key, this.$options.asyncComputed[key]); | ||
for (var key in asyncComputed) { | ||
var getter = getterFn(key, this.$options.asyncComputed[key]); | ||
this.$options.computed[prefix + key] = getter; | ||
} | ||
@@ -65,3 +70,3 @@ | ||
var data = (typeof optionData === 'function' ? optionData.call(this) : optionData) || {}; | ||
for (var _key in this.$options.asyncComputed || {}) { | ||
for (var _key in asyncComputed) { | ||
var item = this.$options.asyncComputed[_key]; | ||
@@ -93,3 +98,3 @@ if (isComputedLazy(item)) { | ||
var promiseId = 0; | ||
_this.$watch(prefix + _key2, function (newPromise) { | ||
var watcher = function watcher(newPromise) { | ||
var thisPromise = ++promiseId; | ||
@@ -104,5 +109,7 @@ | ||
} | ||
setAsyncState(_this.$asyncComputed[_key2], 'updating'); | ||
newPromise.then(function (value) { | ||
if (thisPromise !== promiseId) return; | ||
setAsyncState(_this.$asyncComputed[_key2], 'success'); | ||
_this[_key2] = value; | ||
@@ -112,2 +119,4 @@ }).catch(function (err) { | ||
setAsyncState(_this.$asyncComputed[_key2], 'error'); | ||
_this.$asyncComputed[_key2].exception = err; | ||
if (pluginOptions.errorHandler === false) return; | ||
@@ -123,3 +132,11 @@ | ||
}); | ||
}, { immediate: true }); | ||
}; | ||
_this.$asyncComputed[_key2] = { | ||
exception: null, | ||
update: function update() { | ||
watcher(getterOnly(_this.$options.asyncComputed[_key2])()); | ||
} | ||
}; | ||
setAsyncState(_this.$asyncComputed[_key2], 'updating'); | ||
_this.$watch(prefix + _key2, watcher, { immediate: true }); | ||
}; | ||
@@ -135,2 +152,15 @@ | ||
function setAsyncState(stateObject, state) { | ||
stateObject.state = state; | ||
stateObject.updating = state === 'updating'; | ||
stateObject.error = state === 'error'; | ||
stateObject.success = state === 'success'; | ||
} | ||
function getterOnly(fn) { | ||
if (typeof fn === 'function') return fn; | ||
return fn.get; | ||
} | ||
function getterFn(key, fn) { | ||
@@ -137,0 +167,0 @@ if (typeof fn === 'function') return fn; |
{ | ||
"name": "vue-async-computed", | ||
"version": "3.4.1", | ||
"version": "3.5.0", | ||
"description": "Async computed properties for Vue", | ||
@@ -5,0 +5,0 @@ "main": "dist/vue-async-computed.js", |
122
README.md
@@ -41,3 +41,3 @@ <big><h1 align="center">vue-async-computed</h1></big> | ||
````js | ||
```js | ||
new Vue({ | ||
@@ -60,7 +60,7 @@ data: { | ||
} | ||
```` | ||
``` | ||
Or rather, you could, but it wouldn't do what you'd want it to do. But using this plugin, it works just like you'd expect: | ||
````js | ||
```js | ||
new Vue({ | ||
@@ -77,7 +77,7 @@ data: { | ||
} | ||
```` | ||
``` | ||
This is especially useful with ES7 async functions: | ||
````js | ||
```js | ||
new Vue({ | ||
@@ -92,13 +92,13 @@ asyncComputed: { | ||
}) | ||
```` | ||
``` | ||
## Install | ||
````sh | ||
```sh | ||
npm install --save vue-async-computed | ||
```` | ||
``` | ||
Alternately, you can link it directly from a CDN: | ||
````html | ||
```html | ||
<script src="https://unpkg.com/vue-async-computed"></script> | ||
@@ -109,8 +109,8 @@ <!-- | ||
--> | ||
<script src="https://unpkg.com/vue-async-computed@3.4.1"></script> | ||
```` | ||
<script src="https://unpkg.com/vue-async-computed@3.5.0"></script> | ||
``` | ||
When used with a module system such as `webpack` or `browserify`, you need to explicitly install `vue-async-computed` via `Vue.use()`: | ||
````js | ||
```js | ||
import Vue from 'vue' | ||
@@ -120,3 +120,3 @@ import AsyncComputed from 'vue-async-computed' | ||
Vue.use(AsyncComputed) | ||
```` | ||
``` | ||
@@ -127,3 +127,3 @@ You don't need to do this when using global script tags. So long as you include `vue-async-computed` in a script tag after Vue itself, it will be installed automatically. | ||
````js | ||
```js | ||
import AsyncComputed from 'vue-async-computed' | ||
@@ -171,3 +171,3 @@ | ||
*/ | ||
```` | ||
``` | ||
@@ -182,3 +182,3 @@ [Like with regular synchronous computed properties](https://vuejs.org/guide/computed.html#Computed-Setter), you can pass an object | ||
````js | ||
```js | ||
new Vue({ | ||
@@ -210,3 +210,3 @@ data: { | ||
*/ | ||
```` | ||
``` | ||
@@ -216,3 +216,3 @@ You can instead define the default value as a function, in order to depend on | ||
````js | ||
```js | ||
new Vue({ | ||
@@ -234,11 +234,11 @@ data: { | ||
} | ||
```` | ||
``` | ||
You can also set a custom global default value in the options passed to `Vue.use`: | ||
````javascript | ||
```javascript | ||
Vue.use(AsyncComputed, { | ||
default: 'Global default value' | ||
}) | ||
```` | ||
``` | ||
@@ -254,3 +254,3 @@ ## Recalculation | ||
the property itself has: | ||
````js | ||
```js | ||
@@ -277,2 +277,25 @@ new Vue({ | ||
} | ||
``` | ||
You can trigger re-computation of an async computed property manually, e.g. to re-try if an error occured during evaluation. This should be avoided if you are able to achieve the same result using a watched property. | ||
````js | ||
new Vue({ | ||
asyncComputed: { | ||
blogPosts: { | ||
get () { | ||
return Vue.http.get('/posts') | ||
.then(response => response.data) | ||
}, | ||
} | ||
}, | ||
methods: { | ||
refresh() { | ||
// Triggers an immediate update of blogPosts | ||
// Will work even if an update is in progress. | ||
this.$asyncComputed.blogPosts.update(); | ||
} | ||
} | ||
} | ||
```` | ||
@@ -285,3 +308,3 @@ | ||
you can use `shouldUpdate`: | ||
````js | ||
```js | ||
@@ -310,3 +333,3 @@ new Vue({ | ||
} | ||
```` | ||
``` | ||
@@ -323,3 +346,3 @@ The main advantage over adding an if statement within the get function is that when the computation is | ||
For example: | ||
````js | ||
```js | ||
new Vue({ | ||
@@ -341,5 +364,48 @@ data: { | ||
} | ||
``` | ||
## Computation status | ||
For each async comptued property, an object is added to `$asyncComputed` that contains information about the current computation state of that object. This object contains the following properties: | ||
```js | ||
{ | ||
// Can be one of updating, success, error | ||
state: 'updating', | ||
// A boolean that is true while the property is updating. | ||
updating: true, | ||
// The property finished updating wihtout errors (the promise was resolved) and the current value is available. | ||
success: false, | ||
// The promise was rejected. | ||
error: false, | ||
// The raw error/exception with which the promise was rejected. | ||
exception: null | ||
} | ||
``` | ||
It is meant to be used in your rendering code to display update / error information. | ||
````js | ||
new Vue({ | ||
asyncComputed: { | ||
posts() { | ||
return Vue.http.get('/posts') | ||
.then(response => response.data) | ||
} | ||
} | ||
} | ||
} | ||
// This will display a loading message every time the posts are updated: | ||
// <div v-if="$asyncComputed.posts.updating"> (Re)loading posts </div> | ||
// If you only want to display the message the first times the posts load, you can use the fact that the default value is null: | ||
// <div v-if="$asyncComputed.posts.updating && posts === null"> Loading posts </div> | ||
// You can display an error message if loading the posts failed. | ||
// The vue-resources library passes the error response on to the rejection handler. | ||
// It is therefore available in $asyncComputed.posts.exception | ||
// <div v-else-if="$asyncComputed.posts.error"> Error while loading posts: $asyncComputed.posts.exception.statusText </div> | ||
```` | ||
## Error handling | ||
## Global error handling | ||
@@ -353,3 +419,3 @@ By default, in case of a rejected promise in an async computed property, vue-async-computed will take care of logging the error for you. | ||
````js | ||
```js | ||
Vue.use(AsyncComputed, { | ||
@@ -373,3 +439,3 @@ errorHandler (stack) { | ||
) | ||
```` | ||
``` | ||
@@ -376,0 +442,0 @@ You can pass `false` as the `errorHandler` in order to silently ignore rejected promises. |
32549
366
441