• Stars
    star
    2,004
  • Rank 23,109 (Top 0.5 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created over 7 years ago
  • Updated about 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

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

Multiple Process Loader Management for Vue and (optionally) Vuex.

Read the Medium 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 a very simple idea that manages an array (or Vuex store optionally) with multiple loading states. The built-in loader component listens its registered loader and immediately become loading state.

Quick Start

If you are a try and learn developer, you can start trying the vue-wait now using codesandbox.io.

Edit VueWait Sandbox

1. Install:

yarn add vue-wait

2. Require:

For Vue 2.x

import VueWait from 'vue-wait'

Vue.use(VueWait)

new Vue({
  // your vue config
  wait: new VueWait(),
})

For Vue 3.x

import { createApp } from 'vue'
import { createVueWait } from 'vue-wait'
import App from './App.vue'

const VueWait = createVueWait()

createApp(App)    // Create app with root component
  .use(VueWait)   // Register vue-wait
  .mount('#app')

3. Use in Your Components

<template>
  <v-wait for="my list is to load">
    <template slot="waiting">
      <div>
        <img src="loading.gif" />
        Loading the list...
      </div>
    </template>
    <ul>
      <li v-for="item in myList">{{ item }}</li>
    </ul>
  </v-wait>
</template>

<script>
  export default {
    data() {
      return {
        myList: []
      }
    },
    async created() {
      // start waiting
      this.$wait.start('my list is to load');

      this.myList = await fetch('/my-list-url');

      // stop waiting
      this.$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

import VueWait from 'vue-wait'

Vue.use(VueWait) // add VueWait as Vue plugin

Then you should register wait property (VueWait instance) to the Vue instance:

new Vue({
  el: '#app',
  store,
  wait: new VueWait({
    // Defaults values are following:
    useVuex: false,              // Uses Vuex to manage wait state
    vuexModuleName: 'wait',      // Vuex module name

    registerComponent: true,     // Registers `v-wait` component
    componentName: 'v-wait',     // <v-wait> component name, you can set `my-loader` etc.

    registerDirective: true,     // Registers `v-wait` directive
    directiveName: 'wait',       // <span v-wait /> directive name, you can set `my-loader` etc.

  }),
});

♻️ Usage with Vuex

Simply set useVuex parameter to true and optionally override vuexModuleName

import VueWait from 'vue-wait'

Vue.use(Vuex)
Vue.use(VueWait) // add VueWait as Vue plugin

Then you should register VueWait module:

new Vue({
  el: '#app',
  store,
  wait: new VueWait({
    useVuex: true, // You must pass this option `true` to use Vuex
    vuexModuleName: 'vuex-example-module' // It's optional, `wait` by default.
  }),
});

Now VueWait will use Vuex store for data management which can be traced in Vue DevTools > Vuex

♻️ Usage with Nuxt.js

Add vue-wait/nuxt to modules section of nuxt.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 configuration
  wait: { useVuex: true }
}

🔁 VueWait Options

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, your VueWait methods will be accessible by $w.waits(..) etc.
useVuex Boolean false Use this value for enabling integration with Vuex store. When this value is true VueWait will store data in Vuex store and all changes to this data will be made by dispatching actions to store
vuexModuleName String "wait" Name for Vuex store if useVuex set to true, otherwise not used.
registerComponent Boolean true Registers v-wait component.
componentName String "v-wait" Changes v-wait component name.
registerDirective Boolean true Registers v-wait directive.
directiveName String "v-wait" Changes v-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-bar v-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-bar v-if="$wait.is('creating user')">Creating User...</progress-bar>
</template>

You can use waiting alias instead of is.

<template>
  <div v-if="$wait.waiting('fetching users')">
    Fetching users...
  </div>
</template>

Also you can use matcher to make it more flexible:

Please see matcher library to see how to use matchers.

<template>
  <progress-bar v-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-bar v-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>
  <progress min="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 than 100. If you total is given, current must be bigger than total.

<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>
  <progress min="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-bar v-wait:visible='"creating user"'>Creating User...</progress-bar>
</template>

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

Hides if the given loader is loading.

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

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

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

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

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

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

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

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

Starts given loader on click.

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

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

Ends given loader on click.

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

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

Toggles given loader on click.

<template>
  <button v-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>
  <button v-wait:click.progress='["downloading", 80]'>Set the "downloading" loader to 80</button>
</template>

🔌 Loading Action and Getter Mappers

vue-wait provides mapWaitingActions and mapWaitingGetters mapper to be used with your Vuex stores.

Let's assume you have a store and async actions called createUser and updateUser. 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 map action 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 is wait by default. If you've changed on config, you should get it by rootGetters['<vuex module name>/is'] or rootGetters['<vuex module name>/any'].

You can access vue-wait's Vuex getters using rootGetters in Vuex.

getters: {
  cartOperationInProgress(state, getters, rootState, rootGetters) {
    return rootGetters['wait/is']('cart.*');
  }
},

And you can start and end loaders using wait actions. You must pass root: true option to the dispatch method.

actions: {
  async addItemToCart({ dispatch }, item) {
    dispatch('wait/start', 'cart.addItem', { root: true });
    await CartService.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 default waitFor return async function, if you want to wrap default sync function pass true in last argument

Example using with async function

import { waitFor } from 'vue-wait';

...
methods: {
  fetchDataFromApi: waitFor('fetch data', async function () {
    function sleep(ms) {
      return new Promise(resolve => setTimeout(resolve, ms));
    }
    // do work here
    await sleep(3000);
    // simulate some api call
    this.fetchResponse = Math.random()
  })
}
...

See also examples/wrap-example

💧 Using v-wait Component

If you disable registerComponent option then import and add v-wait into components

import vLoading from 'vue-wait/src/components/v-wait.vue'
components: {
  'v-wait': vLoading
}

In template, you should wrap your content with v-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 a button 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 with v-wait component.

Just pass <transition> props and listeners to the v-wait with transition 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 better user loading experience.

In this example above, the tab gets data from back-end, and the table loads data from back-end at the same time. With vue-wait, you will be able to manage these two seperated loading processes easily:

<template lang='pug'>
  <div>
    <v-wait for="fetching tabs">
      <template slot="waiting">
        <b-tabs>
          <template slot="tabs">
            <b-nav-item active="active" disabled>
              <v-icon name="circle-o-notch" spin="spin" />
            </b-nav-item>
          </template>
        </b-tabs>
      </template>
      <b-tabs>
        <template slot="tabs">
          <b-nav-item v-for="tab in tabs">{{ tab.name }}</b-nav-item>
        </template>
      </b-tabs>
    </v-wait>
    <v-wait for="fetching data">
      <table-gradient-spinner slot="waiting" />
      <table>
        <tr v-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 called my-waiter:

<!-- MySpinner.vue -->
<i18n>
  tr:
    loading: Yükleniyor...
  en:
    loading: Loading...
</i18n>

<template>
  <div class="loading-spinner">
    <v-icon name="refresh" spin="spin" />
    <span>{{ $t('loading') }}</span>
  </div>
</template>

<style scoped lang="scss">
  .loading-spinner {
    opacity: 0.5;
    margin: 50px auto;
    text-align: center;
    .fa-icon {
      vertical-align: middle;
      margin-right: 10px;
    }
  }
</style>

Now you can use your spinner everywhere using slot='waiting' attribute:

<template lang="pug">
  <v-wait for="fetching data">
    <my-waiter slot="waiting" />
    <div>
      <p>My main content after fetching data...</p>
    </div>
  </v-wait>
</template>

📦 Using with external spinner libraries

You can use vue-wait with another spinner libraries like epic-spinners or other libraries. You just need to add slot="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 a v-wait's waiting 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 see Loaders section of vuejs/awesome-vue.

🚌 Run example

Use npm run dev-vuex, npm run dev-vue or npm run dev-wrap commands. for running examples locally.

Testing components

You can test components using vue-wait but it requires configuration. Let's take a basic component for instance:

<v-wait for="loading">
   <Spinner slot="waiting" />
   <ul class="suggestions">
      <li v-for="suggestion in suggestions">{{ suggestion.Name }}</li>
   </ul>
</v-wait>
const localVue = createLocalVue();
localVue.use(Vuex); // optionally when you use Vuex integration

it('uses vue-wait component', () => {
    const wrapper = shallowMount(Suggestions, { localVue });
    expect(wrapper.find('.suggestions').exists()).toBe(true);
});

vue-test-utils will replace v-wait component with an empty div, making it difficult to test correctly.

First, make your local Vue instance use vue-wait,

const localVue = createLocalVue();
localVue.use(Vuex); // optionally when you use Vuex integration
localVue.use(VueWait);

Then inject the wait property using VueWait constructor,

it('uses vue-wait component', () => {
    const wrapper = shallowMount(SuggestedAddresses, {
      localVue,
      wait: new VueWait()
    });
    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

Since vue-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

More Repositories

1

awesome-chatgpt-prompts

This repo includes ChatGPT prompt curation to use ChatGPT better.
HTML
95,527
star
2

graphql.js

A Simple and Isomorphic GraphQL Client for JavaScript
JavaScript
2,256
star
3

omelette

Omelette is a simple, template based autocompletion tool for Node and Deno projects with super easy API. (For Bash, Zsh and Fish)
CoffeeScript
1,355
star
4

delorean

An Agnostic, Complete Flux Architecture Framework
JavaScript
749
star
5

fatura

eArşiv / eFatura Fatura Oluşturucu
JavaScript
548
star
6

vue-smart-route

Smart route search to make intelligent looking apps with Vue.js.
JavaScript
319
star
7

react-wait

Complex Loader Management Hook for React Applications
JavaScript
305
star
8

atom-term2

THIS PROJECT IS NO LONGER MAINTAINED. PLEASE USE ATOM TERM3 PACKAGE
CoffeeScript
294
star
9

guardian

Guardian watches over your files and run assigned tasks.
Crystal
276
star
10

loremjs

Lorem.js Dummy Text/Image Generator for jQuery and Native JS
HTML
189
star
11

react-hooks-todo-app

A highly testable TodoList app that uses React hooks and Context.
JavaScript
187
star
12

kamber

Static site server (basically blogs) with Crystal Language
Crystal
173
star
13

dom-wait

Complex Loader and Progress Management for Vanilla JS
HTML
143
star
14

pq

Human Readable Promise Chains
JavaScript
143
star
15

vue-plugin-boilerplate

Vue Plugin Boilerplate
JavaScript
122
star
16

confirmation

A simple Node tool to replicate Browser's confirm popup on your CLI.
JavaScript
115
star
17

netflix-data

All Netflix Genres
111
star
18

dahi

Türkçe NLP'de bir marka (Parody)
JavaScript
97
star
19

atom-bootstrap3

Twitter Bootstrap 3 Snippets for Atom
CoffeeScript
81
star
20

completion

Easy Command Line Completion for Crystal
Crystal
70
star
21

honst

Fixes your dataset according to your rules.
JavaScript
69
star
22

do-sshuttle

Use DigitalOcean Droplet to Transparent Proxying via sshuttle
Shell
66
star
23

baristai

TypeScript
66
star
24

equiv

It finds equivalents of the things.
Crystal
61
star
25

kemal-react-chat

This demonstrates how easy it is to build Realtime Web applications with Kemal.
JavaScript
60
star
26

temel

Extensible Markup DSL for Crystal
Crystal
59
star
27

omi-devtools

DevTools for Omi.js
JavaScript
54
star
28

vue-analog-clock-range

Vue Analog Clock Range Component
Vue
52
star
29

kreal

Kreal is a model sharing & RPC library built on and works with Kemal seamlessly.
HTML
44
star
30

ufcs

DLang's Universal Function Call Syntax port to JavaScript
JavaScript
42
star
31

xtract

Extract data from DOM, easily.
JavaScript
40
star
32

lambda.cr

Uniformed function call syntax for Crystal Language.
Crystal
36
star
33

qvimrc

Quick Vimrc
Vim Script
33
star
34

deasciifier

Deasciifier yabancı metinleri Türkçeye çevirmenize yarayan bir uygulamadır.
JavaScript
29
star
35

emoji-downloader

A simple Emoji downloader for CLI
Shell
29
star
36

phaser-coffee-boilerplate

Phaser CoffeeScript + Browserify + LiveReload Boilerplate
CoffeeScript
27
star
37

hexwords-tr

Turkish Hex Words
JavaScript
27
star
38

graphql.js-demo

GraphQL.js Sinatra Example
Ruby
24
star
39

macaron

Macros for CoffeeScript
CoffeeScript
24
star
40

postcss-inline-image

PostCSS plugin that puts images as data URIs into your CSS
JavaScript
21
star
41

sofle-mario

C
21
star
42

dont-fail-me-again

Node.js Error Handler by The Dark Side
JavaScript
21
star
43

fka

JavaScript
20
star
44

idiot

Easier NodeMCU Environment - IoT Made Simple
MoonScript
19
star
45

storm-bitbar

BitBar plugin for Storm-SSH
Shell
16
star
46

veznedar

Arapça Kök Üreteci
JavaScript
16
star
47

graphqljs-rails

GraphQL.js Rails for Rails 5
JavaScript
15
star
48

f

14
star
49

view.coffee

Dead Simple, Vanilla-ish Client-Side Framework based on jQuery, inspired by GitHub's Space-Pen Library
CoffeeScript
13
star
50

jquery.resizestop

Special Event for Resize Controls
12
star
51

awesome-safran

Safran CLI Okuyucuları
12
star
52

safran-cli

Safran.io için command line okuyucu.
JavaScript
11
star
53

GitHubDashboard.kdapp

GitHubDashboard
CoffeeScript
11
star
54

twhosts

Twitter Unblock
10
star
55

jackpack

JackPack
JavaScript
9
star
56

backbone-presentation

Backbone.js Presentation (Turkish)
JavaScript
9
star
57

mood-convert-ampt

JavaScript
9
star
58

popthelock

Pop The Lock JS Clone
8
star
59

mertlang

8
star
60

puremise.js

Yet another purely functional Promise Monad implementation
JavaScript
8
star
61

boehm

7
star
62

wolves

JavaScript port of lykoss/lykos, a Werewolf party game IRC bot
CoffeeScript
7
star
63

knockbone

Knockout.js and Backbone.js Entegration
JavaScript
7
star
64

llm-viz-tr

TypeScript
6
star
65

dyncall

Dynamic method calling support for Crystal Language.
Crystal
6
star
66

lama.app

OS X Application Helper for emre/lama
Objective-C
6
star
67

ircbot

IRC Bot Framework
PHP
6
star
68

kurye

GitHub Project Cloner for Boilerplate Projects
Python
6
star
69

r3

Ruby
5
star
70

html5init

HTML5 Project Startup
CSS
5
star
71

ronin

Very simple Jinja2 based Static Site Boilerplate
Python
5
star
72

plug

jquery plugin generator.
JavaScript
5
star
73

vimme

my newest simple vim environment
Vim Script
5
star
74

shelljs

Javascript + PHP ShellLike Application
5
star
75

pinata

Sinatra-like PHP Framework
PHP
5
star
76

twittersearch

Workshop Project for Ozgur Web Gunleri 2012
JavaScript
4
star
77

restafarian

Node.js Restful Client
3
star
78

mvc

PHP
3
star
79

kamber-theme-default

Kamber Default Template
CSS
3
star
80

cssmodules-demo

Ruby
2
star
81

notes

HTML
2
star
82

win-turkish-us-layout

Windows sistemlere OS X'teki `ALT + harf` desteği sağlar.
2
star
83

kamber-theme-dark

Dark theme for Kamber
CSS
2
star
84

dotvim

Vim Script
1
star
85

lolero

Lölero Language
1
star
86

knapsack-problem

Solving Knapsack Problem with dynamic programming in Ruby
Ruby
1
star
87

umut

1
star
88

turkiye-brand-db

The Brands Database for Turkey
1
star
89

keybase-messenger

keybase based crypted local messenger interface running on firebase
1
star
90

jspy

jspy conference 2012 website
1
star
91

md5solve

MD5 decrypter. Generates content from it's MD5 sum. Work in progress.
1
star
92

react-rails-server-side-example

React Rails with Server Side Rendering Example
JavaScript
1
star
93

crystal-kemal-todo-list

TodoList based on Crystal and Kemal
HTML
1
star
94

react-playground

JavaScript
1
star
95

popeye

Popeye.js ~ Backbone.js PowerUp Wrapper
JavaScript
1
star
96

respublica

Complete Vieux Framework - "Res publica non dominetur"
1
star
97

perr

Perr UI Library using CoffeeScript
JavaScript
1
star
98

vim-script-template

Vim Script
1
star
99

dotfiles

Dotfiles for myself
Vim Script
1
star
100

eventstream.js

Pure functional EventStream monad implementation
JavaScript
1
star