@andres-ml/vue-async-operations
v1.1.1
Published
Managing async operations statuses in your Vue components
Downloads
7
Maintainers
Readme
vue-async-operations
Managing async operations statuses in your Vue components
Install
npm install vue-async-operations
Basic usage
import Vue from 'vue'
import VueAsyncOperations from 'vue-async-operations'
Vue.use(VueAsyncOperations)
Then, in your component options, provide an asyncOperations
object where each key is a name of an async operation and the value is a function that returns Promise
:
//...
asyncOperations: {
someAsyncStuff () {
// return Promise
// ☝️vm instance is binded to function as `this` context
}
}
//...
Or you can link operation to some method in component:
//...
asyncOperations: {
someAsyncStuff: 'someMethodName'
}
//...
Then, trigger an operation in your component e.g. in created
hook:
//...
created () {
this.$async.someAsyncStuff.$perform()
}
//...
And use operation performing state in the template:
<div v-if="$async.someAsyncStuff.$pending">
<!-- render a loader while async operation is pending -->
</div>
<!-- OR -->
<div v-if="$async.someAsyncStuff.$resolved">
<!-- some stuff that shouldn't be rendered until async operation finished -->
</div>
<!-- OR -->
<div v-if="$async.someAsyncStuff.$rejected">
<!-- some stuff that should be displayed if async operation failed -->
</div>
Several operations in one
The function that defines an operation may return an array of promises, e.g.:
//...
asyncOperations: {
someCompositAsyncStuff () {
return [
this.someVuexAction(),
this.someAnotherVuexAction()
]
}
}
//...
This way, the operation state handler, that placed under the hood of this plugin, will operates via Promise.all([])
so reactive states of operation will be changed only when last promise will be resolved or rejected.
Also, you can define as much separate operations as you need:
//...
asyncOperations: {
asyncStuff1 () { //... },
asyncStuff2 () { //... },
asyncStuff3 () { //... },
...
}
//...
Passing args
You can pass arguments to $perform()
method and receive them in operation function:
//...
asyncOperations: {
// some operation defined as function
stuff1 (a, b, c) {
console.log(a, b, c) // 1, 2, 3
},
// another operation defined as link to some method of a component
stuff2: 'loadStuff2'
},
methods: {
loadStuff2 (d, e, f) {
console.log(d, e, f) // 4, 5, 6
}
},
created () {
this.$async.stuff1.$perform(1, 2, 3)
this.$async.stuff2.$perform(4, 5, 6)
}
//...
You can access the passed arguments through $args
. This can be useful when you have multiple, similar calls to the same operation:
<button
v-for="(item, index) in items" :key="index"
:disabled="$async.delete.$pending && $async.delete.$args[1] === index"
@click="() => $async.delete.$perform(index)"
>
Delete item #{{ index }}
</button>
Handle operation result
The .$perform()
method of an operation returns Promise
and passes the result of the original promise into resolve
and reject
methods:
//...
created () {
this.$async.someAsyncStuff.$perform().then(
result => { // handle a result },
err => { // handle an error },
finally => { // handle the finish of an operation }
)
}
//...
If your operation returns an array of promises, the result will contain an array of results in the order they were defined in the original array
Also, you can handle the result directly in operation function:
//...
asyncOperations: {
someOperation: 'someMethod'
},
methods: {
someMethod () {
this.$api.someApiCall().then(
result => { // handle the result },
error => { // handle an error }
)
}
},
created () {
this.$async.someOperation.$perform()
}
//...
Operations composing
If you have several async operations that are leading to some common result and you need to track their reactive statuses separately but also you wanna have an aggregated reactive status for whole batch, you can compose your operations the following way:
//...
asyncOperations: {
allAsyncStuff: {
asyncStuff1 () {},
asyncStuff2 () {}
}
}
//...
Then, use separate and aggregated reactive statuses
<div v-if="$async.allAsyncStuff.$pending"> Some stuff is still loading... </div>
<div v-else-if="$async.allAsyncStuff.$resolved"> All stuff loaded </div>
<div v-if="$async.allAsyncStuff.asyncStuff1.$resolved"> Stuff 1 is loaded </div>
<div v-if="$async.allAsyncStuff.asyncStuff2.$resolved"> Stuff 2 is loaded </div>
Performing composed operations with passing args
//...
asyncOperations: {
allAsyncStuff: {
asyncStuff1 (a, b, c) {
console.log(a, b, c) // 1, 2, 3
},
asyncStuff2 ({a, b, c}) {
console.log(a, b, c) // 4, 5, 6
}
}
},
created () {
this.$async.allAsyncStuff.$perform({
asyncStuff1: [1, 2, 3], // pass args to `asyncStuff1`
asyncStuff2: {a: 4, b: 5, c: 6} // pass args to `asyncStuff2`
})
}
//...
Concurrent calls
If $perform
is called multiple consecutive times before the first call has resolved, all of them will resolve/reject, but only the last one will also modify the state ($pending/$rejected/$resolved
).
Each consecutive call will increment $requestId
(which starts at null
before the first call, then 0
, 1
, and so on).
Operation states
$pending
$resolved
$rejected
$err
Operation methods
$perform
Operation context
$args
$requestId
Plugin options
You can customize some stuff:
Vue.use(VueAsyncOperations, {
mixinPrefix,
dataPropName,
computedPropName,
componentOptionName
})
mixinPrefix
- plugin adds to your application a global mixin which injects property with operations states into the componentsdata
and according to official vuejs style guide this property is prefixed, but you can change this prefix if it's necessary for some reasondataPropName
- actually the name of the prop mentioned abovecomputedPropName
- the name of the computed prop you use for getting acces to operations for getting its states and calling$perform()
methodcomponentOptionName
- the name of the component option where you define operations
Plugin defaults are:
{
mixinPrefix: 'vueAsyncOps_',
dataPropName: 'async',
computedPropName: '$async',
componentOptionName: 'asyncOperations'
}