Vue lazy loader

Simple Asynchronous Infinite Scroll with Vue Watchers

by Christopher O

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
<!-- https://scotch.io/tutorials/simple-asynchronous-infinite-scroll-with-vue-watchers -->

<div id="app">
  <section>
    <h1>🍺 Make yourself some Punk Beers 🍻</h1>
    <div v-if="beers.length === 0" class="loading">Loading...</div>
    <div v-for="beer in beers" class="beer-contain">
      <div class="beer-img">
        <img :src="beer.img" height="350" />
      </div>
      <div class="beer-info">
        <h2>{{ beer.name }}</h2>
        <p class="bright">{{ beer.tagline }}</p>
        <p><span class="bright">Description:</span> {{ beer.desc }}</p>
        <p><span class="bright">Tips:</span> {{ beer.tips }}</p>
        <h3 class="bright">Food Pairings</h3>
        <ul>
          <li v-for="item in beer.food">
            {{ item }}
          </li>
        </ul>
      </div>
    </div>
  </section>
</div>

CSS

body {
  font-family: 'Archivo Narrow', sans-serif;
  background: #ffb3ff;
}

h1, h2, h3, h4 {
  font-family: 'Spectral SC', serif;
}

h1 {
  margin-top: 40px;
  color: #000000;
  text-align: center;
}

.loading {
  color: #e6e6e6;
  text-align: center;
  font-size: 20px;
}

.display {
  display: flex;
  justify-content: center;
  align-content: center;
}

#app {
  @extend .display;
  flex-direction: column;
}

.beer-contain {
  @extend .display;
  width: 50%;
  margin: 20px 24%;
  box-shadow: 0 2px 3px 1px rgba(30, 0, 0, 0.1);
}

.beer-img {
  @extend .display;
  padding: 30px;
  background: #99ddff;
  border: 1px solid #88498F;
  border-right: 1px solid #f44822;
}

.beer-info {
  background: #5c8a8a;
  color: white;
  padding: 30px;
  border: 1px solid #88498F;
  .bright {
    color: #fcd7cf;
    font-family: 'Roboto Condensed', sans-serif;
  }
}

h3 {
  margin-bottom: 5px;
}

ul {
  margin-top: 5px;
}

Vue

new Vue({
  el: '#app',
  data() {
    return {
      bottom: false,
      beers: []
    }
  },
    methods: {
    bottomVisible() {
      const scrollY = window.scrollY
      const visible = document.documentElement.clientHeight
      const pageHeight = document.documentElement.scrollHeight
      const bottomOfPage = visible + scrollY >= pageHeight
      return bottomOfPage || pageHeight < visible
    },
    addBeer() {
      axios.get('https://api.punkapi.com/v2/beers/random')
        .then(response => {
          let api = response.data[0];
          let apiInfo = {
            name : api.name,
            desc : api.description,
            img : api.image_url,
            tips : api.brewers_tips,
            tagline : api.tagline,
            food : api.food_pairing
          };
          this.beers.push(apiInfo)
          if (this.bottomVisible()) {
            this.addBeer()
          }
      })
    }
  },
  watch: {
    bottom(bottom) {
      if (bottom) {
        this.addBeer()
      }
    }
  },
  created() {
    window.addEventListener('scroll', () => {
      this.bottom = this.bottomVisible()
    })
    this.addBeer()
  }
})