12. Project [vuejs-10]: [dao] plugin, asynchronous HTTP requests
The directory structure of the [vuejs-10] project is as follows:

The [vuejs-10] project demonstrates a component making an HTTP request to a remote server. The architecture used is as follows:

A [Vue.js] component uses the [dao] layer to communicate with the tax calculation server.
12.1. Installing dependencies
The [vuejs-10] application uses the [axios] library to make asynchronous requests to the tax calculation server. We need to install this dependency:

- in [4-5], the line added to the [package.json] file after installing the [axios] library [1-3];
12.2. The [Dao] class
The [Dao] class is the one developed in the section [The Dao Class]. We include it here for reference:
'use strict';
// imports
import qs from 'qs'
// [Dao] class
class Dao {
// constructor
constructor(axios) {
this.axios = axios;
// session cookie
this.sessionCookieName = "PHPSESSID";
this.sessionCookie = '';
}
// initialize session
async initSession() {
// HTTP request options [get /main.php?action=init-session&type=json]
const options = {
method: "GET",
// URL parameters
params: {
action: 'init-session',
type: 'json'
}
};
// Execute the HTTP request
return await this.getRemoteData(options);
}
async authenticateUser(user, password) {
// HTTP request options [post /main.php?action=authenticate-user]
const options = {
method: "POST",
headers: {
'Content-type': 'application/x-www-form-urlencoded',
},
// POST body
data: qs.stringify({
user: user,
password: password
}),
// URL parameters
params: {
action: 'authenticate-user'
}
};
// Execute the HTTP request
return await this.getRemoteData(options);
}
async getAdminData() {
// HTTP request options [get /main.php?action=get-admindata]
const options = {
method: "GET",
// URL parameters
params: {
action: 'get-admindata'
}
};
// execute the HTTP request
const data = await this.getRemoteData(options);
// result
return data;
}
async getRemoteData(options) {
// for the session cookie
if (!options.headers) {
options.headers = {};
}
options.headers.Cookie = this.sessionCookie;
// execute the HTTP request
let response;
try {
// asynchronous request
response = await this.axios.request('main.php', options);
} catch (error) {
// The [error] parameter is an exception instance—it can take various forms
if (error.response) {
// the server's response is in [error.response]
response = error.response;
} else {
// we re-throw the error
throw error;
}
}
// response is the entire HTTP response from the server (HTTP headers + the response itself)
// retrieve the session cookie if it exists
const setCookie = response.headers['set-cookie'];
if (setCookie) {
// setCookie is an array
// we search for the session cookie in this array
let found = false;
let i = 0;
while (!found && i < setCookie.length) {
// search for the session cookie
const results = RegExp('^(' + this.sessionCookieName + '.+?);').exec(setCookie[i]);
if (results) {
// store the session cookie
// eslint-disable-next-line require-atomic-updates
this.sessionCookie = results[1];
// found
found = true;
} else {
// next element
i++;
}
}
}
// The server's response is in [response.data]
return response.data;
}
}
// export the class
export default Dao;
The [vuejs-10] project uses only the asynchronous [initSession] method in lines 18–30. Note that the [Dao] class is instantiated with an [axios] parameter on line 10, which is initialized by the calling code. In this case, the calling code is the [./main.js] script.
12.3. The [pluginDao] plugin
The [pluginDao] plugin is as follows:
export default {
install(Vue, dao) {
// adds a [$dao] property to the Vue class
Object.defineProperty(Vue.prototype, '$dao', {
// when Vue.$dao is referenced, we return the second parameter [dao]
get: () => dao,
})
}
}
If we recall the explanation given for the [event-bus] plugin, we see that the [pluginDao] plugin creates a new property called [$dao] in the [Vue] class/function. This property will have (as we will show) as its value the object exported by the [./Dao] script, i.e., the previous [Dao] class.
12.4. The main script [main.js]
The code for the main script [main.js] is as follows:
// imports
import Vue from 'vue'
import App from './App.vue'
import axios from 'axios';
// plugins
import BootstrapVue from 'bootstrap-vue'
Vue.use(BootstrapVue);
// Bootstrap
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
// [DAO] layer
import Dao from './Dao';
// axios configuration
axios.defaults.timeout = 2000;
axios.defaults.baseURL = 'http://localhost/php7/scripts-web/impots/version-14';
axios.defaults.withCredentials = true;
// instantiate the [dao] layer
const dao = new Dao(axios);
// [dao] plugin
import pluginDao from './plugins/dao'
Vue.use(pluginDao, dao)
// configuration
Vue.config.productionTip = false
// instantiate project [App]
new Vue({
render: h => h(App),
}).$mount('#app')
The [main.js] script:
- instantiates the [dao] layer on lines 14–21;
- includes the [pluginDao] plugin on lines 24–25;
- line 15: the [Dao] class is imported;
- lines 17–18: the [axios] object, which handles HTTP requests, is configured. This object is imported on line 4;
- line 17: defines a [timeout] of 2 seconds;
- line 18: the URL of the tax calculation server;
- line 19: to enable cookie exchange with the server;
- lines 24-25: use of the [pluginDao] plugin
- line 24: import of the plugin;
- line 25: integration of the plugin. We can see that the second parameter of the [Vue.use] method is the reference to the [dao] layer defined on line 21. This is why the [Vue.$dao] property will refer to the [dao] layer in all instances of the [Vue] class/function, i.e., in all [Vue.js] components;
12.5. The main view [App.vue]
The code for the main view [App] is as follows:
<template>
<div class="container">
<b-card>
<!-- message -->
<b-alert show variant="success" align="center">
<h4>[vuejs-10]: [dao] plugin, asynchronous HTTP requests</h4>
</b-alert>
<!-- component making an asynchronous request to the tax calculation server-->
<Component1 @error="doSomethingWithError" @endWaiting="endWaiting" @beginWaiting="beginWaiting" />
<!-- display of any errors -->
<b-alert show
variant="danger"
v-if="showError">[error] event intercepted by [App]. Value received = {{error}}</b-alert>
<!-- waiting message with a spinner -->
<b-alert show v-if="showWaiting" variant="light">
<strong>Request to the tax calculation server in progress...</strong>
<b-spinner variant="primary" label="Spinning"></b-spinner>
</b-alert>
</b-card>
</div>
</template>
<script>
import Component1 from "./components/Component1";
export default {
name: "app",
// component state
data() {
return {
// controls the loading spinner
showWaiting: false,
// controls the display of the error
showError: false,
// the intercepted error
error: {}
};
},
// components used
components: {
Component1
},
// event handling methods
methods: {
// start waiting
beginWaiting() {
// display waiting status
this.showWaiting = true;
// hide the error message
this.showError = false;
},
// end waiting
endWaiting() {
// hide waiting
this.showWaiting = false;
},
// error handling
doSomethingWithError(error) {
// note that an error occurred
this.error = error;
// display the error message
this.showError = true;
}
}
};
</script>
Comments
- line 9: [Component1] is the component that makes the asynchronous HTTP request. It can emit three events:
- [beginWaiting]: the request is about to be made. A waiting message must be displayed to the user;
- [endWaiting]: the request is complete. The wait must be stopped;
- [error]: the request failed. An error message must be displayed;
- lines 10–13: the alert that displays any error message. It is controlled by the boolean [showError] on line 33. It displays the error on line 35;
- lines 14–18: the alert that displays the waiting message with a spinner. It is controlled by the boolean [showWaiting] on line 47;
- lines 45–50: [beginWaiting] is the method executed upon receiving the [beginWaiting] event. It displays the waiting message (line 47) and hides the error message (line 49) in case it is still visible from a previous operation;
- lines 52–55: [endWaiting] is the method executed upon receiving the [endWaiting] event. It hides the waiting message (line 54);
- lines 57–62: [doSomethingWithError] is the method executed upon receiving the [error] event. It logs the received error (line 59) and displays the error message (line 61);
12.6. The [Component1] component
The code for the [Component1] component is as follows:
<template>
<b-row>
<b-col>
<b-alert show
variant="warning"
v-if="showMsg">Value received from the server = {{data}}</b-alert>
</b-col>
</b-row>
</template>
<script>
export default {
name: "component1",
// component state
data() {
return {
showMsg: false
};
},
// event handling methods
methods: {
// processing data received from the server
doSomethingWithData(data) {
// store the received data
this.data = data;
// display it
this.showMsg = true;
}
},
// the component has just been created
created() {
// Initialize the session with the server - asynchronous request
// we use the promise returned by the [dao] layer methods
// signal the start of the operation
this.$emit("beginWaiting");
// launch the asynchronous operation
this.$dao
// This involves initializing a JSON session with the tax calculation server
.initSession()
// method that processes the received data if successful
.then(data => {
// process the received data
this.doSomethingWithData(data);
})
// method that handles the error if an error occurs
.catch(error => {
// propagate the error to the parent component
this.$emit("error", error.message);
}).finally(() => {
// end of wait
this.$emit("endWaiting");
})
}
};
</script>
Comments
- lines 4–6: The component consists of a single alert that displays the value returned by the tax calculation server, but only if the HTTP request is successful. This alert is controlled by the boolean [showMsg] on line 17;
- lines 31–53: The HTTP request is made as soon as the component is created. Therefore, its code is placed in the [created] method on line 31;
- line 35: the parent component is notified that the asynchronous request is about to start;
- lines 37–39: the [this.$dao.initSession] method is executed. It initializes a JSON session with the tax calculation server. The immediate result of this method is a [Promise];
- lines 41–44: this code runs when the server returns its result without an error. The server’s result is in [data]. On line 43, we call the [doSomethingWithData] method to process this result;
- lines 46–49: this code runs if an error occurs while executing the request. On line 48, the parent component is notified that an error has occurred, and the error message [error.message] is passed to it;
- Lines 49–52: This code runs in all cases. We notify the parent component that the HTTP request is complete;
- Lines 23–28: The [doSomethingWithData] method is responsible for processing the data [data] sent by the server. On line 25, this data is stored, and on line 27, it is displayed;
12.7. Running the project

If the tax calculation server is not running when the project is launched, the following result is obtained:

Let’s start the [Laragon] server (see https://tahe.developpez.com/tutoriels-cours/php7) and reload the page above. The result is then as follows:

Note: We are using version 14 of the tax calculation server defined at https://tahe.developpez.com/tutoriels-cours/php7.