最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to add a loading icon while searching? (Vue.js 2) - Stack Overflow

programmeradmin0浏览0评论

My component is like this :

<template>
    ...
        <input type="text" class="form-control" v-model="rawFilter" placeholder="Search" @keyup="getPlayers">
    ...
</template>

<script>
    import _ from 'lodash'
    ...
    export default {
        ...
        data() {
            return{
                msg:'hello vue',
                rawFilter:'',
                loading:false
            }
        },
        ...
        methods: {
            getPlayers: _.debounce(function(e) {
                const text = e.target.value.trim()
                this.$store.dispatch('getPlayers', {
                  q: text
                })
            },1000),
            ...
        }
    }
</script>

When I searching, before display the data, I want add a loading icon

How can I do it in vue.js 2?

My component is like this :

<template>
    ...
        <input type="text" class="form-control" v-model="rawFilter" placeholder="Search" @keyup="getPlayers">
    ...
</template>

<script>
    import _ from 'lodash'
    ...
    export default {
        ...
        data() {
            return{
                msg:'hello vue',
                rawFilter:'',
                loading:false
            }
        },
        ...
        methods: {
            getPlayers: _.debounce(function(e) {
                const text = e.target.value.trim()
                this.$store.dispatch('getPlayers', {
                  q: text
                })
            },1000),
            ...
        }
    }
</script>

When I searching, before display the data, I want add a loading icon

How can I do it in vue.js 2?

Share Improve this question asked Feb 25, 2017 at 9:03 samuel tohsamuel toh 7,07624 gold badges76 silver badges110 bronze badges
Add a comment  | 

3 Answers 3

Reset to default 10

For usability's sake, I'll suggest using a loader which has its own vuex state.

  1. This will allow you to have control over it from any component.
  2. You can use it easily by means of simple function calls.
  3. Naturally avoid props and events.

First define where you would need this particular loader:

  1. Is it to be used for all api calls?
  2. Some browser intensive task (like processing an loaded file).
  3. Or something more specific in nature (maybe show the loader only when the user is trying to login)

If your loader is not tightly coupled to any component like in case 1. Then it would make more sens to keep your loader in your main vue file (if you are using vue-cli then App.vue)

Something like so:

<template>
  <div id="app">
    <loader></loader>
    <router-view></router-view>
  </div>
</template>

<script>
import Loader from './components/shared/loader/Loader'

export default {
  name: 'app',
  components: {
    Loader
  }
}
</script>

With this, you don't have to add loader.vue in every other component file. But first, I'll show you the loader component and store I am using.

<template>
  <div class='loader-container' :class='{"show": show, "hidden": !show}'>
    <div class="curved-div">
      <div class="colour-magic">
        <i class='fa fa-circle-o-notch rotate'></i>
      </div>
      <div class="loading">
        {{ loading }}
      </div>
    </div>
  </div>
</template>

<script>
import { mapGetters } from 'vuex'
import * as NameSpace from '../../../store/NameSpace'

export default {
  data () {
    return {
      loading: 'Loading...'
    }
  },
  computed: {
    ...mapGetters({
      show: NameSpace.GET_LOADER_STATE
    })
  }
}
</script>

<style scoped>
.loader-container {
  position: fixed;
  width: 100%;
  height: 100%;
  background: rgba(0,0,0,0.8);
}

.curved-div {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translateX(-50%);
  border-radius: .3rem;
  width: 20rem;
  padding:1rem;
  background: white;
  box-shadow: 0 0 .1rem #fefefe;
}

.curved-div > * {
  display: inline-block;
}

.rotate {
  border-radius: 50%;
  padding: .5rem;
  animation-name: rotate;
  animation-duration: .7s;
  animation-iteration-count: infinite;
  animation-delay: 0s;
}

.loading {
  text-align: center;
  width: 12rem;
  font-size: 1.8rem;
}

.show {
  visibility: visible;
  opacity: 1;
  z-index: 1;
  transition: opacity 0.5s ease-out, visibility 0.5s ease-out, z-index 0.5s ease-out;
}

.hidden {
  opacity: 0;
  visibility: hidden;
  z-index: 0;
  transition: opacity 0.5s ease-out, visibility 0.5s ease-out, z-index 0.5s ease-out;
}

@keyframes rotate {
  0% {
    transform: rotateZ(0deg);
  }
  100% {
    transform: rotateZ(360deg);
  }
}

.colour-magic {
  animation-name: colorMagic;
  animation-duration: 20s;
  animation-iteration-count: infinite;
  animation-delay: 0s;
}

@keyframes colorMagic {
  0% { color: rgb(179,10,10); }
  10% { color: rgb(227,132,22); }
  20% { color: rgb(164,153,7); }
  30% { color: rgb(26,171,19); }
  40% { color: rgb(19,144,177); }
  50% { color: rgb(14,16,221); }
  60% { color: rgb(27,9,98); }
  70% { color: rgb(58,11,111); }
  80% { color: rgb(126,14,129); }
  90% { color: rgb(208,19,121); }
  100% { color: rgb(198,18,18); }
}
</style>

Please note that I am using font-awesome for the loader.

and here is the store:

import * as NameSpace from '../NameSpace'
// you can also use the namespace: true in your store and eliminate the need of NameSpace.js    

const state = {
  [NameSpace.LOADER_STATE]: false
}

const getters = {
  [NameSpace.GET_LOADER_STATE]: state => {
    return state[NameSpace.LOADER_STATE]
  }
}

const mutations = {
  [NameSpace.MUTATE_LOADER_STATE]: (state, payload) => {
    state[NameSpace.LOADER_STATE] = payload
  }
}

const actions = {
  [NameSpace.LOADER_SHOW_ACTION]: ({ commit }, payload) => {
    commit(NameSpace.MUTATE_LOADER_STATE, payload)
  }
}

export default {
  state,
  getters,
  mutations,
  actions
}

A usage example:

// This is not a .vue file it is a .js file, therefore a different way of using the store.
import Vue from 'vue'
import * as NameSpace from 'src/store/NameSpace'
import loaderState from 'src/store/modules/loader'

/**
* Pass the mutation function to reduce the text length
* This function can now be used in the api calls to start/stop the loader
* as the api starts and finishes.
*/
let loaderSwitch = loaderState.mutations[NameSpace.MUTATE_LOADER_STATE].bind(null, loaderState.state)

login (username, password) {
  loaderSwitch(true)
  return new Promise((resolve, reject) => {
    SomeEndpoint.logIn(username, password, {
      success (user) {
        loaderSwitch(false)
        resolve(user.attributes)
      },
      error (user, error) {
        loaderSwitch(false)
        reject(errorHelper(error.code))
      }
    })
  })

Now, irrespective of the component where login is used, the loader component need not be kept there.

You simply need to attach it to a flag and use v-if, if you're using vue resource, you can set the loading flag to true in the before callback and set it back to false after you receive the response:

Vue Instance

  methods: {
    loadData() {
      this.$http.get('/search', {
        before: () => {
          this.loading = true;
        }
      }).then(response => {
        // Deal with response
      }).then(() => {
          //set loading flag to false
          this.loading = false;
      })
    }
  },
  data: {
    loading: false
  }

HTML

<div id="app">
  <button @click="loadData">
    Get Data
  </button>

  <!-- Only show if loading is true -->
  <div v-if="loading" v-cloak>
    <i class="fa fa-circle-o-notch fa-spin fa-3x fa-fw"></i>
    <span>Loading...</span>
  </div>
</div>

Here's the JSFiddle: https://jsfiddle.net/hyeycoan/

You could add a loading div on your template, and toggle it's display based on loading flag. Something like this

new Vue({
  el: '#app',
  data: {
    show: true,
	isLoading: false,  
  },
	methods:{
		loadData: function(){
			this.isLoading = true;
			setTimeout(function(){
				this.isLoading = false;
			}.bind(this),1000);
		}
	}
})
.loading{
	display: none;
	position: absolute;
    width: 100%;
    height: 100%;
    top: 0;
    background: rgba(128, 128, 128, 0.5);
}
.loading.show{
	display: initial;
}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
<div id="app">
	<div class="loading" v-bind:class="{ show: isLoading }">
		<span>Loading</span>
	</div>
	<button @click="loadData">Load</button>
</div>	
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.js"></script>
</body>
</html>

You could make the loading indicator pretty with css, or you can use already available one, for example you could use http://tobiasahlin.com/spinkit/, http://loading.io/

发布评论

评论列表(0)

  1. 暂无评论