Skip to content

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:

html
<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:

html
<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:

html
<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:

html
<!-- 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:

js
// 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:

html
<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:

html
<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:

html
<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:

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