Configure Load Messages
This component provides four different slots that you can use to display different load messages: spinner
, no-more
, no-results
, error
. All of the default values are listed in the preview container on the right, and you can read more about them here.
Via Component Prop
Only the spinner
slot can be configured via the prop, and the set value can only be the built-in spinner type:
<infinite-loading spinner="spiral"></infinite-loading>
You can preview all built-in spinner types on the right. Please use other ways if you want to create your own spinner.
Via v-slot
Directive
We can use the [v-slot
directive] (https://vuejs.org/guide/components/slots.html) to configure them:
<infinite-loading>
<template #spinner>
<div>Loading...</div>
<template>
<template #no-more>
<div>No more message</div>
<template>
<template #no-results>
<div>No results message</div>
<template>
</infinite-loading>
Unlike other slots, the default value for the error
slot will provide a retry button for users to load the data again. If you want to implement a retry button for users when you customize the error
slot, you can receive the retry method trigger
in prop and inject it into the retry button. like this:
<infinite-loading>
<template #error="{ trigger }">
<div>
Error message, click <a href="#retry" @click.prevent="trigger">here</a> to retry
</div>
</template>
</infinite-loading>
Via Plugin API
In order to maintain consistent behavior for all load messages when we are building a large application, this plugin supports configuration on all slots using the plugin API. We just need to pass a string or Vue component to it, click here to read more about that.
The error
slot is still special in this way. Just as with the v-slot
directive, if you want to implement a retry button for users in your own error component, you can use the vm.$attrs
property, like this:
<!-- your own error component -->
<div>
Error message, click
<a href="#retry" @click.prevent="$attrs.trigger">here</a>
to retry
</div>
If you want to keep variables clear, you can also define a function property named trigger
, and bind it to your retry button:
// your own error component
export default {
/* ... */
props: {
trigger: Function,
},
/* ... */
};
About Hide & Default Styles
For easy use, this component provides some default styles (font-size
, color
and padding
) for slot content. If you want to keep all default styles when you configure via the v-slot
directive, you have to wrap the content with a template
tag:
<infinite-loading>
<!-- The no-more message will has default styles -->
<template v-slot:no-more>No more message</template>
</infinite-loading>
If you want to hide a slot, you can create an empty element that is not a template
element, because the empty template
element will be ignored by Vue.js:
<infinite-loading>
<!-- The no-more slot will not be displayed -->
<span v-slot:no-more></span>
</infinite-loading>
If you want to remove all default styles to avoid affecting your own styles, you can wrap the content with an element that is not a template
element:
<infinite-loading>
<!-- The no-more message will has no default styles -->
<div v-slot:no-more>No more message</div>
</infinite-loading>
I almost forgot, if you want to configure the slot content globally via the plugin API, you can control it like this:
import Vue from 'vue';
import InfiniteLoading from '@codog/vue3-infinite-loading';
import InfiniteError from 'path/to/your/components/InfiniteError',
Vue.use(InfiniteLoading, {
slots: {
// keep default styles
noResults: 'No results message',
// remove default styles
noMore: InfiniteError,
// hide slot
error: {
render: h => h('div'),
},
},
});
Demo
Built-in spinner types:
<template>
<div class="tab">
<a
href="javascript:;"
@click="changeType('spinners')"
:class="{ active: type === 'spinners' }">
spinners
</a>
<a
href="javascript:;"
@click="changeType('no-more')"
:class="{ active: type === 'no-more' }">
no-more
</a>
<a
href="javascript:;"
@click="changeType('no-results')"
:class="{ active: type === 'no-results' }">
no-results
</a>
<a
href="javascript:;"
@click="changeType('error')"
:class="{ active: type === 'error' }">
error
</a>
</div>
<infinite-loading :spinner="spinner" @infinite="infiniteHandler" ref="infiniteLoadingRef"></infinite-loading>
<div class="spinner-types" v-show="type === 'spinners'">
<h3>Built-in spinner types:</h3>
<button @click="changeSpinner('default')" :class="{ active: spinner === 'default' }">default</button>
<button @click="changeSpinner('spiral')" :class="{ active: spinner === 'spiral' }">spiral</button>
<button @click="changeSpinner('circles')" :class="{ active: spinner === 'circles' }">circles</button>
<button @click="changeSpinner('bubbles')" :class="{ active: spinner === 'bubbles' }">bubbles</button>
<button @click="changeSpinner('waveDots')" :class="{ active: spinner === 'waveDots' }">waveDots</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
import InfiniteLoading from '../../lib'
const type = ref('spinners')
const spinner = ref('default')
const infiniteLoadingRef = ref(null)
const infiniteHandler = ($state) => {
if (type.value === 'error') {
setTimeout(() => {
$state.error();
}, 1000);
}
}
const changeType = (v) => {
const stateChanger = infiniteLoadingRef.value.stateChanger
type.value = v;
switch (v) {
case 'spinners':
stateChanger.reset();
break;
case 'no-more':
stateChanger.reset();
setTimeout(() => {
stateChanger.loaded();
stateChanger.complete();
}, 1);
break;
case 'no-results':
stateChanger.reset();
setTimeout(() => {
stateChanger.complete();
}, 1);
break;
case 'error':
stateChanger.error();
break;
default:
}
}
const changeSpinner = (v) => {
spinner.value = v;
}
</script>
<style lang="scss" scoped>
.tab {
display: flex;
margin-bottom: 20px;
font-size: 14px;
background-color: #fff;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1);
a {
flex: 1;
display: block;
padding: 0 5px;
color: #666;
line-height: 40px;
text-align: center;
text-decoration: none;
&.active {
color: #4777a5;
box-shadow: 0 -3px 0 0 #4777a5 inset;
}
}
}
.spinner-types {
/* position: absolute; */
top: 150px;
left: 0;
right: 0;
padding: 0 12px;
h3 {
margin: 0 0 5px 4px;
font-size: 16px;
color: #666;
}
button {
margin: 8px 4px;
min-width: 88px;
padding: 8px 14px;
color: #fff;
font-size: 14px;
background: linear-gradient(30deg, #3f5b77, #51779b);
border: 0;
border-radius: 2px;
cursor: pointer;
outline: none;
transition: 0.2s all;
overflow: hidden;
&,
&:active {
box-shadow: 0 12px 30px -10px rgba(28, 90, 160, 0.5);
}
&:not(:active):not(.active):hover {
transform: translateY(-1px);
box-shadow: 0 13px 30px -10px rgba(28, 90, 160, 0.5);
}
&.active {
position: relative;
&::before {
content: '\2714';
position: absolute;
bottom: 0;
right: 0;
width: 0;
height: 0;
color: #51779b;
font-size: 12px;
line-height: 10px;
border: 12px solid transparent;
border-right-color: #fff;
border-bottom-color: #fff;
}
}
}
}
</style>