Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
/vue-waitPublic

Complex Loader and Progress Management for Vue/Vuex and Nuxt Applications

License

NotificationsYou must be signed in to change notification settings

f/vue-wait

Repository files navigation

Multiple Process Loader Management forVue and (optionally)Vuex.

Read theMedium post "Managing Complex Waiting Experiences on Web UIs".

npm version


vue-wait

Play with demo above.

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.

⏩Quick Start

If you are atry and learn developer, you can start trying thevue-wait now usingcodesandbox.io.

Edit VueWait Sandbox

1. Install:

yarn add vue-wait

2. Require:

For Vue 2.x

importVueWaitfrom'vue-wait'Vue.use(VueWait)newVue({// your vue configwait:newVueWait(),})

For Vue 3.x

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')

3. Use in Your Components

<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.

▶️Detailed Start

📦 Requirements

🚀 Power Supplies

  • Vuex, optionally (v2.0.0+)

🔧 Installation

via CLI:

$ yarn add vue-wait# or if you using npm$ npm install vue-wait

via Vue UI:

📖 Usage

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.}),});

♻️ Usage with Vuex

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

♻️ Usage with Nuxt.js

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}}

🔁VueWait Options

You can use this options for customize VueWait behavior.

Option NameTypeDefaultDescription
accessorNameString"$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.
useVuexBooleanfalseUse 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
vuexModuleNameString"wait"Name forVuex store ifuseVuex set to true, otherwise not used.
registerComponentBooleantrueRegistersv-wait component.
componentNameString"v-wait"Changesv-wait component name.
registerDirectiveBooleantrueRegistersv-wait directive.
directiveNameString"v-wait"Changesv-wait directive name.

🌈 Global Template Helpers

vue-wait provides some helpers to you to use in your templates.All features can be obtained from $wait property in Vue components.

.any

Returns boolean value if any loader exists in page.

<template>  <progress-barv-if="$wait.any">Please wait...</progress-bar></template>

.is(loader String | Matcher) or.waiting(loader String | Matcher)

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>

.is(loaders Array<String | Matcher>) or.waiting(loaders Array<String | Matcher>)

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>

.start(loader String)

Starts the given loader.

<template>  <button@click="$wait.start('creating user')">Create User</button></template>

.end(loader String)

Stops the given loader.

<template>  <button@click="$wait.end('creating user')">Cancel</button></template>

.progress(loader String, current [, total = 100])

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>
Completing the Progress

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>

.percent(loader String)

Returns the percentage of the given loader.

<template>  <progressmin="0"max="100":value="$wait.percent('downloading')" /></template>

🏹 Directives

You can use directives to make your template cleaner.

v-wait:visible='"loader name"'

Shows if the given loader is loading.

<template>  <progress-barv-wait:visible='"creating user"'>Creating User...</progress-bar></template>

v-wait:hidden='"loader name"' orv-wait:visible.not='"loader name"'

Hides if the given loader is loading.

<template>  <mainv-wait:hidden='"creating *"'>Some Content</main></template>

v-wait:disabled='"loader name"'

Setsdisabled="disabled" attribute to element if the given loader is loading.

<template>  <inputv-wait:disabled="'*'"placeholder="Username" />  <inputv-wait:disabled="'*'"placeholder="Password" /></template>

v-wait:enabled='"loader name"' orv-wait:disabled.not='"loader name"'

Removesdisabled="disabled" attribute to element if the given loader is loading.

<template>  <buttonv-wait:enabled='"creating user"'>Abort Request</button></template>

v-wait:click.start='"loader name"'

Starts given loader on click.

<template>  <buttonv-wait:click.start='"create user"'>Start loader</button></template>

v-wait:click.end='"loader name"'

Ends given loader on click.

<template>  <buttonv-wait:click.end='"create user"'>End loader</button></template>

v-wait:toggle='"loader name"'

Toggles given loader on click.

<template>  <buttonv-wait:toggle='"flip flop"'>Toggles the loader</button></template>

v-wait:click.progress='["loader name", 80]'

Sets the progress of given loader on click.

<template>  <buttonv-wait:click.progress='["downloading", 80]'>Set the "downloading" loader to 80</button></template>

🔌 Loading Action and Getter Mappers

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'},]),},// ...

☢️Advanced Getters and Actions Usage

The Vuex module name iswait by 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});}},

waitFor(loader String, func Function [,forceSync = false])

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

💧 Usingv-wait Component

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>

🔁 Transitions

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>

⚡️ Making Reusable Loader Components

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>

📦 Using with external spinner libraries

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.

🚌 Run example

Usenpm run dev-vuex,npm run dev-vue ornpm run dev-wrap commands.for running examples locally.

✔ Testing components

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!});

For Development on vue-wait

Install packages

$ yarn install# or if you using npm$ npm install

Bundle it

$ yarn bundle# or if you using npm$ npm run bundle

🎯 Contributors

  • Fatih Kadir Akın, (creator)
  • Igor, (maintainer, made Vuex-free)

🔗 Other Implementations

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.

🔑 License

MIT ©Fatih Kadir Akın


[8]ページ先頭

©2009-2025 Movatter.jp