Today I’m going to discuss implementing a loading icon like you see in a lot of SPAs within the context of a Nuxt project I’m working on - Form Cast, a simple form backend-as-a-service.
The Icon
My project at the moment is pretty spartanly designed, with a plain white background. For my loading icon, I wanted something with a flash of both activity and color. After surfing around, I decided on this (it’s technically a typing activity icon, but who cares).
Let’s go through the entirety of this (at the moment) dead simple page component. This is in our Nuxt app at /pages/forms/index.vue
.
First in our <script>
tag we’re going to import our Forms
component, our listForms
function, which is simply an axios
function that hits our API and returns an array of form object
s, and our mapState
function for our vuex
store, which will allow us to pull our userToken
out of local storage and make an API call to an authentication-protected resource.
<script>
import Forms from '@components/Forms.vue'
import listForms from "@assetsPath/js/services/form/list.js"
import { mapState } from 'vuex';
Now into the next section, our actual component. We’ll use our default
layout and register our Forms
component, will registering two data
keys - forms
(our array of form data) and loading
(our loading icon UI variable, which we’ll set initially to true
since the page will initially be loading). We’ll also use the mapState
function to pull out our userToken
JWT.
export default {
layout: 'default',
components: {
Forms,
},
data: function() {
return {
forms: null,
loading: true
}
},
computed: {
...mapState([
"userToken"
])
},
Now we can just call the listForms
function, passing in our auth token, and once we get everything back, set loading to false
. We’ll need to make the mounted()
function async
so that we can use await
. Then we can end things with a closing </script>
tag.
async mounted() {
this.forms = await listForms(this.userToken);
this.loading = false;
},
</script>
So now we have a loading
state variable being set to true
or false
depending on our API call - we just need to build some display elements around it.
For this we can use Vue’s v-if
, simply showing the loading icon if loading
is true
and wrapping our actual content in a v-else
<div>
.
<template>
<section class="register-page container">
<div class="loading" v-if="loading">
<img class="loading-icon" src="/loading.gif">
</div>
<div v-else>
<div class="section-title-pane">
<h1 class="section-title">My Forms</h1>
</div>
<Forms v-bind:forms="forms" />
</div>
</section>
</template>
Putting it all together now. We’ll also add in a nifty flexbox
trick we can use to both horizontally and vertically center the loading icon, which we’ll set to a width of 100px
.
<script>
import Forms from '@components/Forms.vue'
import listForms from "@assetsPath/js/services/form/list.js"
import { mapState } from 'vuex';
export default {
layout: 'default',
components: {
Forms,
},
data: function() {
return {
forms: null,
loading: true
}
},
computed: {
...mapState([
"userToken"
])
},
async mounted() {
this.forms = await listForms(this.userToken);
this.loading = false;
},
}
</script>
<template>
<section class="register-page container">
<div class="loading" v-if="loading">
<img class="loading-icon" src="/loading.gif">
</div>
<div v-else>
<div class="section-title-pane">
<h1 class="section-title">My Forms</h1>
</div>
<Forms v-bind:forms="forms" />
</div>
</section>
</template>
<style lang="scss">
.loading {
display: flex;
align-items: center;
justify-content: center;
min-height:500px;
}
.loading-icon {
width: 100px;
}
</style>
And we’re done!
Subscribe for more updates
Want to hear more about Form Cast as it goes from glint-in-its-founders-eyes to a real-live business? Subscribe for notification updates below.