Tutorial

Simple Asynchronous Infinite Scroll with Vue Watchers

Draft updated on Invalid Date
Default avatar

By Chris Nwamba

Simple Asynchronous Infinite Scroll with Vue Watchers

This tutorial is out of date and no longer maintained.

Introduction

Infinite Scroll is a UX pattern that suggests showing users few contents on page or app load. More contents are then loaded once the user starts scrolling down the page. These contents are loaded asynchronously by making a request to the server responsible for providing the content.

In this post, I’m going to talk about asynchronous operations in JavaScript, as well as how Vue ‒ an awesome JavaScript framework, can be used in this context. In the process, we will see a simple page that uses infinite scroll.

Understanding Asynchronous Operations

In programs written synchronously, here’s the deal ‒ you’ve got two lines of codes, L1 and L2. L2 cannot execute until L1 has finished running. check out the most basic example of a synchronous written program below:

console.log("ping");
console.log("response");
console.log("pong");
Output
ping response pong

The problem with writing code this way is that it makes your app’s UX rigid and awkward. Let’s say you visit a shopping website, if you’re not running an ad blocker chances are you’ll be flooded with ads. If the e-commerce site was running on synchronously written code, your page would freeze up repeatedly while all of the images loaded for the pages and ads and you wouldn’t be able to click on anything until after the page has finished loading. If the page loaded a lot in the background it could take a while before you could interact with the page which would be very inconvenient.

Enter asynchronous programs, they offer us a much better deal, I think. You’ve got two lines of code, L1 and L2, where L1 schedules some task to be run in the future but L2 runs before that task completes. Check out this basic demo of writing asynchronous code:

console.log "ping"
$http.get("our_url", (data) => console.log "response")
console.log "pong"
Output
ping pong response

Normally we would see the code above executed in the order in which it was written but because the $http.get request above will take some time to get data from our_url, JavaScript won’t wait for that, instead, it goes ahead and executes the next line of code while waiting for $http.get to finish what it is doing thus the arrangement of logging in the console. Other ways of writing code asynchronously include:

setTimeout functions which schedule something to happen in the future and then go ahead to execute the next block of code:

console.log("Can I take your order?");
setTimeout(function() {
  console.log("Yes please!");
}, 2000);
console.log("Sure, one second please");
Output
Can I take your order? Sure, one second please Yes please!

Higher order functions (also known as callback functions) which are functions that are passed to other functions as parameters. Let’s assume a callback function named function X, it is then passed to another function called function Y as a parameter. Eventually function X is executed or called inside function Y. Check out the code block below:

  function Y(X) {
    $.get("our_url", X);
  }

Callback functions exist in different modes, chances are you’ve used them without knowing. Examples include window.onclick, setTimeout and setInterval.

What are Watchers?

Vue watchers allow us to perform asynchronous operations in response to changing data. They’re like a more general way to react to data changes in your Vue instance. In our Vue Instance, watchers are denoted with the watch keyword and are used thus:

new Vue({
  el: '#app',
  data: {
    // data we intend to bind to our view
  },
  watch: {
    // asynchronous operations that we'll be working with in our app
  }
});

Extending Asynchronous Operations with Watchers

Let’s see how Vue uses watchers to monitor asynchronous operations. Using Vue, we’ll build an app with an infinite scroll feature, once a user gets to the bottom of the page, a GET request is performed and more data is retrieved. Let me just chip in here that this article works through Sarah Drasner’s original pen. I’m a big fan. Let’s get to work:

Import the necessary tools via the DOM, using script tags. Here we’ll import Vue and Axios, a promise-based HTTP client for the browser

<head>
  <script src="https://unpkg.com/vue"></script>
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>

Create a new Vue Instance:

  new Vue({
    // The el property points to the DOM using a query selector
    el: '#app',
  });

Create the data function and append any data that should be bound to the DOM. Initially, we won’t be at the bottom of the page thus the bottom boolean property is set to false. Whatever results our API fetches will be stored in the beers property, which is an empty array.

data() {
  return {
    bottom: false,
    beers: []
  }
}

Define our methods using the methods member. Methods allow us to create functions and bind events to these functions as well as handle these events. In the bottomVisible() function, we use three read-only properties to manually create our infinite scroll feature:

  1. scrollY: Returns the Y coordinate of the top edge of the current viewport. If there is no viewport, the returned value is zero.
  2. clientHeight: The inner height of an element in pixels, including padding but not the horizontal scrollbar height, border, or margin.
  3. scrollHeight: The height of an element’s content, including content not visible on the screen due to overflow.

In the addBeer() function, we perform a GET request using Axios. Using promises and callbacks, we create an object apiInfo and pass in the values retrieved from our API call. Every function in our Vue Instance can access data properties using this.

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()
        }
      })
  }
}

The watch option watches for changes to the state of the app and updates the DOM accordingly:

watch: {
  bottom(bottom) {
    if (bottom) {
      this.addBeer()
    }
  }
}

We use the created lifecycle hook to add a scroll event that fires each time the bottomVisible() function is called. To implement our infinite scroll feature, we equate the bottom boolean property in the data function to the bottomVisible() function. created hooks allow us to access reactive data as well as functions in our Vue instance.

created() {
  window.addEventListener('scroll', () => {
    this.bottom = this.bottomVisible()
  })
  this.addBeer()
}

Move over to the DOM, where the following Vue directives will be used to provide two way binding between the DOM and the Vue instance:

  1. v-if: For conditionally rendering statements on the DOM
  2. v-for: For rendering a list of items based on an array, requires a special syntax in the form of beer in beers, where beers is the source data array and beer is an alias for the array element being iterated on.
<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>

Modified Pen

Original Pen by Sarah Drasner

Conclusion

One might ask “Why don’t we just use computed properties for this?”, the reason is that computed properties are synchronous and must return a value. When carrying out asynchronous operations such as timeout functions, or like our demo above, GET requests, it’s best to use watchers because Vue listens for the return value of the function. It’s also cool to use event listeners but these have the disadvantage of manually handling the event and calling a method instead of just listening to data changes. In any case, you now know how to handle asynchronous operations when you see or want to use them in Vue.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about us


About the authors
Default avatar
Chris Nwamba

author

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel