Skip to content

Use With Filter/Tabs

The loading process is exactly the same as in the previous example. The key point here is how to reset the component when we change the filter or tabs. The infinite loading component will reset itself whenever the identifier property has changed. It sounds easy, so let's do it!

html
<header>
  <!-- Hacker News header -->
  <select v-model="newsType" @change="changeType">
    <!-- Type options -->
  </select>
</header>

<div v-for="(item, $index) in list" :key="$index">
  <!-- Hacker News item loop -->
</div>

<infinite-loading :identifier="infiniteId" @infinite="infiniteHandler"></infinite-loading>

In the template, we add a select element and listen for its change event. For the InfiniteLoading component, we add an identifier property.

js
import axios from 'axios';

const api = '//hn.algolia.com/api/v1/search_by_date';

export default {
  data() {
    return {
      page: 1,
      list: [],
      newsType: 'story',
      infiniteId: +new Date(),
    };
  },
  methods: {
    infiniteHandler($state) {
      axios.get(api, {
        params: {
          page: this.page,
          tags: this.newsType,
        },
      }).then(({ data }) => {
        if (data.hits.length) {
          this.page += 1;
          this.list.push(...data.hits);
          $state.loaded();
        } else {
          $state.complete();
        }
      });
    },
    changeType() {
      this.page = 1;
      this.list = [];
      this.infiniteId += 1;
    },
  },
};

In the script, we set default values for the select and identifier properties, then add the type parameter in the API request logic, and we create the changeType method to reset the list data and infinite loading component. Please note, we must change the identifier property after we empty the list. Otherwise, the component may not trigger the infinite event immediately after reset.

That's all, you're done!

Demo

Hacker News
<template>
  <header class="hacker-news-header">
    <a target="_blank" href="http://www.ycombinator.com/">
      <img src="https://news.ycombinator.com/y18.svg">
    </a>
    <span>Hacker News</span>
    <select v-model="newsType" @change="changeType()">
      <option value="story">Story</option>
      <option value="poll">Poll</option>
      <option value="show_hn">Show hn</option>
      <option value="ask_hn">Ask hn</option>
      <option value="front_page">Front page</option>
    </select>
  </header>
  <div class="container">
    <div
      class="hacker-news-item"
      v-for="(item, $index) in list"
      :key="$index"
      :data-num="$index + 1">
      <a target="_blank" :href="item.url" v-text="item.title"></a>
      <p>
        <span v-text="item.points"></span>
        points by
        <a
          target="_blank"
          :href="`https://news.ycombinator.com/user?id=${item.author}`"
          v-text="item.author"></a>
        |
        <a
          target="_blank"
          :href="`https://news.ycombinator.com/item?id=${item.objectID}`"
          v-text="`${item.num_comments} comments`"></a>
      </p>
    </div>
    <infinite-loading :identifier="infiniteId" @infinite="infiniteHandler"></infinite-loading>
  </div>
</template>


<script setup>
import { ref } from 'vue'
import axios from 'axios'
// import InfiniteLoading from '@'
import InfiniteLoading from '../../lib'

const page = ref(0)
const list = ref([])
const api = '//hn.algolia.com/api/v1/search_by_date?tags=story';
const newsType = ref('story')
const infiniteId = ref(new Date())

const infiniteHandler = ($state) => {
  axios.get(api, {
    params: {
      page: page.value,
    },
  }).then(({ data }) => {
    if (data.hits.length) {
      page.value += 1;
      list.value.push(...data.hits);
      $state.loaded();
    } else {
      $state.complete();
    }
  });
}

const changeType = () => {
  page.value = 1
  list.value = []
  infiniteId.value += 1
}
</script>

<style lang="scss" scoped>
.hacker-news-header {
  /* position: fixed; */
  top: 0;
  left: 0;
  right: 0;
  padding: 4px 20px;
  line-height: 14px;
  background-color: #FF6600;

  img {
    display: inline;
    border: 1px solid #fff;
    vertical-align: middle;
  }

  span {
    font-family: Verdana, Geneva, sans-serif;
    font-size: 14px;
    font-weight: bold;
    vertical-align: middle;
  }

  select {
    float: right;
    color: #fff;
    background-color: transparent;
    border: 1px solid #fff;
    outline: none;
  }
}

.container {
  max-height: 400px;
  overflow: auto;
}

.hacker-news-item {
  $gap: 40px;

  margin: 10px 0;
  padding: 0 10px 0 $gap;
  line-height: 16px;
  font-size: 14px;

  &::before {
    content: attr(data-num) '.';
    float: left;
    margin-left: -$gap;
    width: $gap - 8px;
    color: #888;
    text-align: right;
  }

  > a {
    color: #333;
    text-decoration: none;

    &:hover {
      color: #000;
    }
  }

  p {
    margin: 0;
    font-size: 12px;

    &,
    a {
      color: #888;
    }

    a:not(:hover) {
      text-decoration: none;
    }
  }
}
</style>