Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork101
Complex Loader and Progress Management for Vue/Vuex and Nuxt Applications
License
f/vue-wait
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
Multiple Process Loader Management forVue and (optionally)Vuex.
Read theMedium post "Managing Complex Waiting Experiences on Web UIs".
vue-wait helps to manage multiple loading states on the page without any conflict. It's based on avery simple idea that manages an array (or Vuex store optionally) with multiple loading states. Thebuilt-in loader component listens its registered loader and immediately become loading state.
If you are atry and learn developer, you can start trying thevue-wait now usingcodesandbox.io.
yarn add vue-wait
importVueWaitfrom'vue-wait'Vue.use(VueWait)newVue({// your vue configwait:newVueWait(),})
import{createApp}from'vue'import{createVueWait}from'vue-wait'importAppfrom'./App.vue'constVueWait=createVueWait()createApp(App)// Create app with root component.use(VueWait)// Register vue-wait.mount('#app')
<template> <v-waitfor="my list is to load"> <template slot="waiting"> <div> <imgsrc="loading.gif" /> Loading the list... </div> </template> <ul> <liv-for="item in myList">{{ item }}</li> </ul> </v-wait></template><script>exportdefault {data() {return { myList: [] } },asynccreated() {// start waitingthis.$wait.start('my list is to load');this.myList=awaitfetch('/my-list-url');// stop waitingthis.$wait.end('my list is to load'); }, };</script>
vue-wait has more abilities to make the management easier, please read the complete documentation.
- Vue.js (v2.0.0+)
- Vuex, optionally (v2.0.0+)
via CLI:
$ yarn add vue-wait# or if you using npm$ npm install vue-waitvia Vue UI:
importVueWaitfrom'vue-wait'Vue.use(VueWait)// add VueWait as Vue plugin
Then you should registerwait property (VueWait instance) to the Vue instance:
newVue({el:'#app', store,wait:newVueWait({// Defaults values are following:useVuex:false,// Uses Vuex to manage wait statevuexModuleName:'wait',// Vuex module nameregisterComponent:true,// Registers `v-wait` componentcomponentName:'v-wait',// <v-wait> component name, you can set `my-loader` etc.registerDirective:true,// Registers `v-wait` directivedirectiveName:'wait',// <span v-wait /> directive name, you can set `my-loader` etc.}),});
Simply setuseVuex parameter totrue and optionally overridevuexModuleName
importVueWaitfrom'vue-wait'Vue.use(Vuex)Vue.use(VueWait)// add VueWait as Vue plugin
Then you should registerVueWait module:
newVue({el:'#app', store,wait:newVueWait({useVuex:true,// You must pass this option `true` to use VuexvuexModuleName:'vuex-example-module'// It's optional, `wait` by default.}),});
NowVueWait will useVuex store for data management which can be traced inVue DevTools > Vuex
Addvue-wait/nuxt to modules section ofnuxt.config.js
{modules:[// Simple usage'vue-wait/nuxt'// Optionally passing options in module configuration['vue-wait/nuxt',{useVuex:true}]],// Optionally passing options in module top level configurationwait:{useVuex:true}}
You can use this options for customize VueWait behavior.
| Option Name | Type | Default | Description |
|---|---|---|---|
accessorName | String | "$wait" | You can change this value to rename the accessor. E.g. if you rename this to$w, yourVueWait methods will be accessible by$w.waits(..) etc. |
useVuex | Boolean | false | Use this value for enabling integration withVuex store. When this value is trueVueWait will store data inVuex store and all changes to this data will be made by dispatching actions to store |
vuexModuleName | String | "wait" | Name forVuex store ifuseVuex set to true, otherwise not used. |
registerComponent | Boolean | true | Registersv-wait component. |
componentName | String | "v-wait" | Changesv-wait component name. |
registerDirective | Boolean | true | Registersv-wait directive. |
directiveName | String | "v-wait" | Changesv-wait directive name. |
vue-wait provides some helpers to you to use in your templates.All features can be obtained from $wait property in Vue components.
Returns boolean value if any loader exists in page.
<template> <progress-barv-if="$wait.any">Please wait...</progress-bar></template>
Returns boolean value if given loader exists in page.
<template> <progress-barv-if="$wait.is('creating user')">Creating User...</progress-bar></template>
You can usewaiting alias instead ofis.
<template> <divv-if="$wait.waiting('fetching users')"> Fetching users... </div></template>
Also you can use matcher to make it more flexible:
Please seematcher library to see how to use matchers.
<template> <progress-barv-if="$wait.is('creating.*')">Creating something...</progress-bar></template>
Returns boolean value if some of given loaders exists in page.
<template> <progress-barv-if="$wait.is(['creating user', 'page loading'])">Creating User...</progress-bar></template>
Starts the given loader.
<template> <button@click="$wait.start('creating user')">Create User</button></template>
Stops the given loader.
<template> <button@click="$wait.end('creating user')">Cancel</button></template>
Sets the progress of the given loader.
<template> <progressmin="0"max="100":value="$wait.percent('downloading')" /> <button@click="$wait.progress('downloading', 10)">Set progress to 10</button> <button@click="$wait.progress('downloading', 50)">Set progress to 50</button> <button@click="$wait.progress('downloading', 50, 200)">Set progress to 50 of 200 (25%)</button></template>
To complete the progress,current value should be set bigger than100.If youtotal is given,current must be bigger thantotal.
<button @click="$wait.progress('downloading',101)">Set as downloaded (101 of 100)</button>
or
<button @click="$wait.progress('downloading',5,6)">Set as downloaded (6 of 5)</button>
Returns the percentage of the given loader.
<template> <progressmin="0"max="100":value="$wait.percent('downloading')" /></template>
You can use directives to make your template cleaner.
Shows if the given loader is loading.
<template> <progress-barv-wait:visible='"creating user"'>Creating User...</progress-bar></template>
Hides if the given loader is loading.
<template> <mainv-wait:hidden='"creating *"'>Some Content</main></template>
Setsdisabled="disabled" attribute to element if the given loader is loading.
<template> <inputv-wait:disabled="'*'"placeholder="Username" /> <inputv-wait:disabled="'*'"placeholder="Password" /></template>
Removesdisabled="disabled" attribute to element if the given loader is loading.
<template> <buttonv-wait:enabled='"creating user"'>Abort Request</button></template>
Starts given loader on click.
<template> <buttonv-wait:click.start='"create user"'>Start loader</button></template>
Ends given loader on click.
<template> <buttonv-wait:click.end='"create user"'>End loader</button></template>
Toggles given loader on click.
<template> <buttonv-wait:toggle='"flip flop"'>Toggles the loader</button></template>
Sets the progress of given loader on click.
<template> <buttonv-wait:click.progress='["downloading", 80]'>Set the "downloading" loader to 80</button></template>
vue-wait providesmapWaitingActions andmapWaitingGetters mapper to be used with your Vuex stores.
Let's assume you have a store and asyncactions calledcreateUser andupdateUser.It will call the methods you map and will start loaders while action is resolved.
import{mapWaitingActions,mapWaitingGetters}from'vue-wait'// ... methods:{ ...mapWaitingActions('users',{getUsers:'loading users',createUser:'creating user',updateUser:'updating user',}),},computed:{ ...mapWaitingGetters({somethingWithUsers:['loading users','creating user','updating user',],deletingUser:'deleting user',}),}// ...
You can also mapaction to custom method and customize loader name like in example below:
import{mapWaitingActions,mapWaitingGetters}from'vue-wait'// ...methods:{ ...mapWaitingActions('users',{getUsers:{action:'getUsers',loader:'loading users'},createUser:{action:'createUser',loader:'creating user'},createSuperUser:{action:'createUser',loader:'creating super user'},}),},// ...
There is also possibility to use array as a second argument to mapWaitingActions:
// ...methods:{ ...mapWaitingActions('users',['getUsers',{method:'createUser',action:'createUser',loader:'creating user'},{method:'createSuperUser',action:'createUser',loader:'creating super user'},]),},// ...
The Vuex module name is
waitby default. If you've changed on config, you should get it byrootGetters['<vuex module name>/is']orrootGetters['<vuex module name>/any'].
You can accessvue-wait's Vuex getters usingrootGetters in Vuex.
getters:{cartOperationInProgress(state,getters,rootState,rootGetters){returnrootGetters['wait/is']('cart.*');}},
And you can start and end loaders usingwait actions. You must passroot: true option to thedispatch method.
actions:{asyncaddItemToCart({ dispatch},item){dispatch('wait/start','cart.addItem',{root:true});awaitCartService.addItem(item);dispatch('wait/end','cart.addItem',{root:true});}},
Decorator that wraps function, will trigger a loading and will end loader after the original function (func argument) is finished.
By defaultwaitFor return async function, if you want to wrap default sync function passtrue in last argument
Example using with async function
import{waitFor}from'vue-wait';...methods:{fetchDataFromApi:waitFor('fetch data',asyncfunction(){functionsleep(ms){returnnewPromise(resolve=>setTimeout(resolve,ms));}// do work hereawaitsleep(3000);// simulate some api callthis.fetchResponse=Math.random()})}...
See alsoexamples/wrap-example
If you disableregisterComponent option then import and addv-wait into components
importvLoadingfrom'vue-wait/src/components/v-wait.vue'components:{'v-wait':vLoading}
In template, you should wrap your content withv-wait component to show loading on it.
<v-wait for='fetching data'> <template slot='waiting'> This will be shown when "fetching data" loader starts. </template> This will be shown when "fetching data" loader ends.</v-wait>
Better example for abutton with loading state:
<button :disabled='$wait.is("creating user")'> <v-wait for='creating user'> <template slot='waiting'>Creating User...</template> Create User </v-wait></button>
You can use transitions withv-wait component.
Just pass<transition> props and listeners to thev-wait withtransition prop.
<v-wait for="users" transition="fade" mode="out-in" :duration="1000" enter-active-class="enter-active" @leave='someAwesomeFinish()' > <template slot="waiting"> <p>Loading...</p> </template> My content</v-wait>
With reusable loader components, you will be able to use custom loader components as example below. This will allow you to create betteruser loading experience.
In this example above, thetab gets data from back-end, and thetable loads data from back-end at the same time. Withvue-wait, you will be able to manage these two seperated loading processes easily:
<template lang='pug'> <div> <v-waitfor="fetching tabs"> <templateslot="waiting"> <b-tabs> <templateslot="tabs"> <b-nav-itemactive="active"disabled> <v-iconname="circle-o-notch"spin="spin" /> </b-nav-item> </template> </b-tabs> </template> <b-tabs> <templateslot="tabs"> <b-nav-itemv-for="tab in tabs">{{ tab.name }}</b-nav-item> </template> </b-tabs> </v-wait> <v-waitfor="fetching data"> <table-gradient-spinnerslot="waiting" /> <table> <trv-for="row in data"> <!-- ...--> </tr> </table> </v-wait> </div></template>
You may want to design your own reusable loader for your project. You better create a wrapper component calledmy-waiter:
<!-- MySpinner.vue--><i18n> tr: loading: Yükleniyor... en: loading: Loading...</i18n><template> <divclass="loading-spinner"> <v-iconname="refresh"spin="spin" /> <span>{{ $t('loading') }}</span> </div></template><style scoped lang="scss">.loading-spinner {opacity:0.5;margin:50pxauto;text-align:center;.fa-icon {vertical-align:middle;margin-right:10px; } }</style>
Now you can use your spinner everywhere usingslot='waiting' attribute:
<template lang="pug"> <v-waitfor="fetching data"> <my-waiterslot="waiting" /> <div> <p>My main content after fetching data...</p> </div> </v-wait></template>
You can usevue-wait with another spinner libraries likeepic-spinners or other libraries. You just need to addslot="waiting" to the component and Vue handles rest of the work.
First register the component,
import{OrbitSpinner}from'epic-spinners';Vue.component('orbit-spinner',OrbitSpinner);
Then use it in your as av-wait'swaiting slot.
<v-wait for='something to load'> <orbit-spinner slot='waiting' :animation-duration="1500" :size="64" :color="'#ff1d5e'" /></v-wait>
... and done!
For other libraries you can use, please seeLoaders section ofvuejs/awesome-vue.
Usenpm run dev-vuex,npm run dev-vue ornpm run dev-wrap commands.for running examples locally.
You can test components usingvue-wait but it requires configuration. Let's take a basic component for instance:
<v-wait for="loading"> <Spinner slot="waiting" /> <ul> <li v-for="suggestion in suggestions">{{ suggestion.Name }}</li> </ul></v-wait>
constlocalVue=createLocalVue();localVue.use(Vuex);// optionally when you use Vuex integrationit('uses vue-wait component',()=>{constwrapper=shallowMount(Suggestions,{ localVue});expect(wrapper.find('.suggestions').exists()).toBe(true);});
vue-test-utils will replacev-wait component with an emptydiv, making it difficult to test correctly.
First, make your local Vue instance usevue-wait,
constlocalVue=createLocalVue();localVue.use(Vuex);// optionally when you use Vuex integrationlocalVue.use(VueWait);
Then inject thewait property usingVueWait constructor,
it('uses vue-wait component',()=>{constwrapper=shallowMount(SuggestedAddresses,{ localVue,wait:newVueWait()});expect(wrapper.find('.suggestions').exists()).toBe(true);// it works!});
Install packages
$ yarn install# or if you using npm$ npm installBundle it
$ yarn bundle# or if you using npm$ npm run bundle- Fatih Kadir Akın, (creator)
- Igor, (maintainer, made Vuex-free)
Sincevue-wait based on a very simple idea, it can be implemented on other frameworks.
- react-wait: Multiple Process Loader Management for React.
- dom-wait: Multiple Process Loader Management for vanilla JavaScript.
MIT ©Fatih Kadir Akın
About
Complex Loader and Progress Management for Vue/Vuex and Nuxt Applications
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.



