Sending AJAX Requests in Vue.js

What is AJAX?

Asynchronous Javascript and XML (AJAX), is a way of communicating to a web server from a client-side application through the HTTP or HTTPS protocol.

Even though AJAX holds XML in the name, the way data is sent through requests or received doesn't have to be XML, but also plain text, or in most cases JSON, due to it being lighter and a part of JavaScript in and of itself.

Why is AJAX Useful?

Vue.js is used as a front-end framework, and if you ever want to communicate with a server, to retrieve or store information to a database or perform some calculations on your data you will most likely need AJAX.

Even though AJAX can be used by creating an XMLHttpRequest object, which will be available through the browser. There are certain packages that can help us communicate with our server.

Vue.js has an official package which is called vue-resource which works as an HTTP client, but the official documentation suggests using Axios.

Installing Axios and Setting up a Component

Axios is an HTTP client and it can be installed into our package.json dependencies via your package manager of choice, be it NPM or Yarn:

$ npm install axios
OR
$ yarn add axios

After the installation, we can set up a minimalistic test component for demonstration purposes:

// Test.vue
<template>
  <div/>
</template>

<script>
export default {
  data() {
    return {};
  };
}
</script>

Using Axios in Vue Components

Requests to a server over HTTP take a certain amount of time to return with a response, and, to make sure the information shown to the user is correct, promises will have to be used to make sure the data has returned before continuing with our code execution.

To use it in a component, we simply import the package and assign it to a variable:

// Test.vue
<script>
import axios from "axios";

export default {
  data() {
    return {};
  }
};
</script>

Generally, API calls are performed inside the mounted Vue lifecycle hook.

We are going to be using a mock JSON data service called JSONPlaceholder. This is a simulation for what a back-end endpoint will look like when hitting a REST API:

// Test.vue
<script>
import axios from "axios";

export default {
  data() {
    return {};
  },
  mounted() {
    axios.get("https://jsonplaceholder.typicode.com/todos/")
  }
};
</script>

In the code example above, the component is rendered before the information from JSONPlaceholder has arrived. Therefore, the usage of promises will make sure we can handle the data and add it to our view.

Handling Responses with then()

The then() function, lets us manipulate our promise and tell our code what we are going to do if/when the request was successful. We can, for example, with a callback function, check our response in the console:

mounted() {
  axios.get("https://jsonplaceholder.typicode.com/todos/")
    .then(response => console.log(response))
}

browser_console_picture

A response has been returned, and we can see it inside the browser's console, but, how do we show the user the todos list?

We should hold the todos list inside the data object for future referencing inside the DOM.

We will call it todosList, and we will copy the data property of the response object in an immutable way (so that we do not change the actual response object) by using the Javascript spread operator and just show the first 10 elements, using the slice array function, so that we do not load hundreds of todos:

<script>
import axios from "axios";

export default {
  data() {
    return {
      todosList: []
    };
  },
  mounted() {
    axios.get("https://jsonplaceholder.typicode.com/todos/")
      .then(response => {
         this.todosList = [...response.data].slice(0, 10)
      })
  }
};
</script>

Handling Errors with catch()

What happens if the request fails? The state of the application will have to be managed accordingly. For that we have the catch() function, which can be nested to our function stack:

mounted() {
  axios.get("https://jsonplaceholder.typicode.com/todos/")
    .then(response => console.log(response))
    .catch(err => {
       // Manage the state of the application if the request 
       // has failed      
     })
}

This way, we see the error being prompted, and we can do many things with it, like show an error message to the user as an alert or show a card saying "No Todos Available". The possibilities are endless.

Showing the Todos

Using the v-for Vue directive we can inject our todos inside the template and add some styling for visual purposes:

<template>
  <div>
    <ul 
      class="test-list" 
      v-for="todo in todosList" 
      :key="todo.id"
    >
      <li class="test-list--item">
        {{ todo.id }}
      </li>
    </ul>
  </div>
</template>

<script>
import axios from "axios";

export default {
  data() {
    return {
      todosList: []
    };
  },
  mounted() {
    axios.get("https://jsonplaceholder.typicode.com/todos/")
      .then(response => {
         this.todosList = [...response.data].slice(0, 10)
      })
      .catch(err => {
         // Manage the state of the application if the request 
         // has failed      
      })
  }
};
</script>

<style>
.test-list {
  font-family: Roboto;
  list-style: none;
  margin: 20px auto;
  width: 50%;
}

.test-list--item {
  border: 1px solid rgb(41, 41, 41);
  border-radius: 5px;
  text-align: center;
  display: block;
  box-shadow: 2px 2px rgba(138, 124, 124, 0.4);
}

.test-list--id {
  font-weight: 300;
  margin: 10px auto;
}

.test-list--title {
  font-weight: 500;
  margin: 20px auto;
  text-transform: capitalize;
}

.test-list--complete {
  font-weight: 600;
  margin: 10px auto;
  color: #56ca86;
}

.test-list--incomplete {
  font-weight: 600;
  margin: 10px auto;
  color: #ca5656;
}
</style>

If we check our browser, we will see something like this:

http_request_sent

Congratulations, we have just sent an HTTP request to an API and displayed the data to the user!

Async/Await vs Promises

Async/Await is a relatively new language construct of JavaScript which makes our asynchronous code a lot cleaner.

Even though Async/Await calls will be transpiled to promises in the end, it conveys a different approach since we no longer need to setup callback functions for our code, we all know how much we hate those!

Our example code, using Async/Await would look something like the following, while adding some object destructuring magic to make our code look even better:

// Test.vue
async mounted() {
    const url = 'https://jsonplaceholder.typicode.com/todos/'
    const { data } = await axios.get(url)
    this.todosList = [...data].slice(0, 10)
}

The mounted lifecycle hook is prefixed with the async keyword. This ensures that the hook returns a promise.

Within the hook, the await keyword makes the function called with it wait for its resolution to continue.

Conclusion

AJAX is a key part of modern web development, and, even though it may look daunting at first, having it in your arsenal is a must.

The very nature of Vue.js makes you think about your code asynchronously so you can update your components properly, thus giving your users an awesome experience.

For more information on asynchronous code for Vue, you can check the Vue.js CookBook.

Author image
About Lucas Otero
Buenos Aires, Argentina Website