vue-busy-module
VueBusy (vue-busy-module)
Application busy/loading state management as a Vuex module.
- Written for Vue 3 and Vuex 4
- Native Typescript support
- Dead simple, fully-tested implementation. Bring your own loader/spinner.
Installation
yarn add vue-busy-module
Basic Usage
1. Add the plugin to your Vuex store
import VueBusyModule from "vue-busy-module";
export default createStore({
/* ... */
plugins: [VueBusyModule],
});
2. Dispatch VueBusy actions to indicate the start & completion of activities
actions: {
async fetchItems({ dispatch }, payload) {
dispatch("busy/start", { name: "fetching-items" });
try {
const response = await MyApp.fetchService(/* ... */)
/* ... */
dispatch("busy/finish", { name: "fetching-items", outcome: "success" });
} catch (e) {
// Both `outcome` and `data` are optional
dispatch("busy/finish", { name: "fetching-items", outcome: "fail", data: { error: e } });
}
}
}
3. Use Vuex helpers in templates to control your custom "Loading" UI elements
This package intentionally does not come with any loaders or spinner UI elements (though you can borrow the
Spinner component in the example app if you want).
<template>
<Spinner v-if="isBusy('fetching-items')" />
</template>
<script lang="ts">
export default defineComponent({
computed: {
...mapGetters({
isBusy: "busy/isBusy",
}),
}
});
</script>
See more code samples in example Vue app.
Other examples
Provide extra data
when starting and finishing activities
Allows you to pass basic activity metadata to loading/waiting UI without coupling it to business logic.
NOTE: data
is reset whenever an activity is (re-)started. Finish data is merged with start data.
// in your store
dispatch("busy/start", { name: "get-songs", data: { albumName: "The Wall" } });
dispatch("busy/finish", { name: "get-songs", outcome: "success", data: { numSongs: 26 } });
<!-- in your Vue template -->
<div v-if="isBusy('get-songs')">
Fetching songs for album {{ getData('get-songs').albumName }}...
</div>
<div v-else-if="getOutcome('get-songs') === 'success'">
Got {{ getData('get-songs').numSongs }} for album {{ getData('get-songs').albumName }}.
</div>
Group related activities together using your own naming convention
Getters getBusy
and getFinished
accept "*"
wildcards and return matching activities.
<!-- in your Vue template -->
<div v-if="albumFetches.length > 0">
Currently fetching {{ albumFetches.length }} records...
</div>
// in component:
computed: {
...mapGetters({
getBusyActivities: "busy/getBusy",
}),
albumFetches() {
this.getBusyActivities("fetch/album/*");
}
}
// in store:
dispatch("busy/start", { name: "fetch/album/the-wall" })
dispatch("busy/start", { name: "fetch/album/dark-side-of-the-moon" })
dispatch("busy/start", { name: "fetch/album/wish-you-were-here" })
API
The Vuex module is namespaced busy
, i.e. busy/isBusy
, busy/start
, etc.
Actions
busy/start
Indicates an activity has started. Accepts payload:
name
(required) - unique name of the activitydata
(optional) - object hash of activity metadata
Once an activity is started, it is never removed from the state tree. This is by design.
Thus, each time start
is dispatched with the same name, the data
and start time are overwritten.
busy/finish
Indicates an activity has finished. Accepts payload:
name
(required) - unique name of the activityoutcome
(optional) - string to capture activity status/resultdata
(optional) - object hash of activity metadata, merged withdata
from originalstart
dispatch
The underlying FINISH
mutation will throw an error if the activity does not exist in the state tree. But, once
it does exist (i.e. it's started for the first time), there is no enforcement of call sequence. Dispatching finish
on an already finished activity will overwrite its outcome
and finish time and will append data
.
Getters
// Returns true if activity is in progress (started but not yet finished).
// Returns false if activity does not exist or has finished.
// NOTE: `name` must be exact – use `getBusy` for wildcard matching instead.
isBusy(name: string) => boolean;
// Returns data object for a particular activity.
// Returns undefined if activity doesn't exist or data was not provided.
getData(name: string) => unknown;
// Returns outcome string for a particular activity.
// Returns undefined if activity doesn't exist or no outcome was specified.
getOutcome(name: string) => string | undefined;
// Returns difference between finish time and start time
getDuration(name: string) => number | undefined;
// Returns names of all activities which finished with a specific outcome.
// Pass `null` to get finished activities where NO outcome was specified.
filterByOutcome(outcome: string | null) => string[];
// Returns names of all unfinished activities.
// Pass `nameFilter` argument with one or more asterisk wildcards (*) to filter results.
getBusy(nameFilter?: string) => string[];
// Returns names of all finished activities.
// Pass `nameFilter` argument with one or more asterisk wildcards (*) to filter results.
getFinished(nameFilter?: string) => string[];
Development
yarn test
- Run test suite
yarn serve
- Serve example app at localhost:8000
yarn build
- Build dist