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:;"
      :class="{ active: type === 'spinners' }"
      @click="changeType('spinners')"
    >
      spinners
    </a>
    <a
      href="javascript:;"
      :class="{ active: type === 'no-more' }"
      @click="changeType('no-more')"
    >
      no-more
    </a>
    <a
      href="javascript:;"
      :class="{ active: type === 'no-results' }"
      @click="changeType('no-results')"
    >
      no-results
    </a>
    <a
      href="javascript:;"
      :class="{ active: type === 'error' }"
      @click="changeType('error')"
    >
      error
    </a>
  </div>
  <infinite-loading
    ref="infiniteLoadingRef"
    :spinner="spinner"
    @infinite="infiniteHandler"
  />
  <div
    v-show="type === 'spinners'"
    class="spinner-types"
  >
    <h3>Built-in spinner types:</h3>
    <button
      :class="{ active: spinner === 'default' }"
      @click="changeSpinner('default')"
    >
      default
    </button>
    <button
      :class="{ active: spinner === 'spiral' }"
      @click="changeSpinner('spiral')"
    >
      spiral
    </button>
    <button
      :class="{ active: spinner === 'circles' }"
      @click="changeSpinner('circles')"
    >
      circles
    </button>
    <button
      :class="{ active: spinner === 'bubbles' }"
      @click="changeSpinner('bubbles')"
    >
      bubbles
    </button>
    <button
      :class="{ active: spinner === 'waveDots' }"
      @click="changeSpinner('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>