12. Proyecto [vuejs-10]: plugin [dao], solicitudes asíncronas HTTP
La estructura del proyecto [vuejs-10] es la siguiente:

El proyecto [vuejs-10] muestra un componente que realiza una solicitud HTTP a un servidor remoto. La arquitectura utilizada es la siguiente:

Un componente [Vue.js] utiliza la capa [dao] para comunicarse con el servidor de cálculo de impuestos.
12.1. Instalación de las dependencias
La aplicación [vuejs-10] utiliza la biblioteca [axios] para realizar las consultas asíncronas al servidor de cálculo de impuestos. Debemos instalar esta dependencia:

- en [4-5], la línea añadida al archivo [package.json] tras la instalación de la biblioteca [axios] [1-3] ;
12.2. La clase [Dao]
La clase [Dao] es la que se desarrolló en el apartado [La classe Dao]. La reproducimos aquí a modo de recordatorio:
'use strict';
// importaciones
import qs from 'qs'
// clase [Dao]
class Dao {
// constructor
constructor(axios) {
this.axios = axios;
// cookie de sesión
this.sessionCookieName = "PHPSESSID";
this.sessionCookie = '';
}
// iniciar sesión
async initSession() {
// opciones de la solicitud HHTP [get /main.php?action=init-session&type=json]
const options = {
method: "GET",
// parámetros de la consulta URL
params: {
action: 'init-session',
type: 'json'
}
};
// ejecución de la consulta HTTP
return await this.getRemoteData(options);
}
async authentifierUtilisateur(user, password) {
// opciones de la solicitud HHTP [post /main.php?action=authentifier-utilisateur]
const options = {
method: "POST",
headers: {
'Content-type': 'application/x-www-form-urlencoded',
},
// cuerpo del POST
data: qs.stringify({
user: user,
password: password
}),
// parámetros de la URL
params: {
action: 'authentifier-utilisateur'
}
};
// ejecución de la consulta HTTP
return await this.getRemoteData(options);
}
async getAdminData() {
// opciones de la consulta HHTP [get /main.php?action=get-admindata]
const options = {
method: "GET",
// parámetros de la consulta URL
params: {
action: 'get-admindata'
}
};
// ejecución de la consulta HTTP
const data = await this.getRemoteData(options);
// resultado
return data;
}
async getRemoteData(options) {
// para la cookie de sesión
if (!options.headers) {
options.headers = {};
}
options.headers.Cookie = this.sessionCookie;
// ejecución de la solicitud HTTP
let response;
try {
// solicitud asíncrona
response = await this.axios.request('main.php', options);
} catch (error) {
// el parámetro [error] es una instancia de excepción; puede adoptar diversas formas
if (error.response) {
// la respuesta del servidor se encuentra en [error.response]
response = error.response;
} else {
// se vuelve a generar el error
throw error;
}
}
// response es el conjunto de la respuesta HTTP del servidor (encabezados HTTP + la propia respuesta)
// se recupera la cookie de sesión si existe
const setCookie = response.headers['set-cookie'];
if (setCookie) {
// setCookie es una matriz
// se busca la cookie de sesión en esta matriz
let trouvé = false;
let i = 0;
while (!trouvé && i < setCookie.length) {
// se busca la cookie de sesión
const results = RegExp('^(' + this.sessionCookieName + '.+?);').exec(setCookie[i]);
if (results) {
// se almacena la cookie de sesión
// eslint-disable-next-line require-atomic-updates
this.sessionCookie = results[1];
// la hemos encontrado
trouvé = true;
} else {
// siguiente elemento
i++;
}
}
}
// la respuesta del servidor está en [response.data]
return response.data;
}
}
// exportación de la clase
export default Dao;
El proyecto [vuejs-10] solo utiliza el método asíncrono [initSession] de las líneas 18-30. Recordemos que la clase [Dao] se instancia con un parámetro [axios], línea 10, parámetro inicializado por el código llamante. Este código llamante será aquí el script [./main.js].
12.3. El plugin [pluginDao]
El plugin [pluginDao] es el siguiente:
export default {
install(Vue, dao) {
// añade una propiedad [$dao] a la clase Vue
Object.defineProperty(Vue.prototype, '$dao', {
// cuando se hace referencia a Vue.$dao, se devuelve el segundo parámetro [dao]
get: () => dao,
})
}
}
Si recordamos la explicación dada para el plugin [event-bus], vemos que el plugin [pluginDao] crea en la clase/función [Vue] una nueva propiedad llamada [$dao]. Esta propiedad tendrá (queda por demostrar) como valor el objeto exportado por el script [./Dao], es decir, la clase [Dao] anterior.
12.4. El script principal [main.js]
El código del script principal [main.js] es el siguiente:
// importaciones
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'
// capa [dao]
import Dao from './Dao';
// configuración de Axios
axios.defaults.timeout = 2000;
axios.defaults.baseURL = 'http://localhost/php7/scripts-web/impuestos/versión-14';
axios.defaults.withCredentials = true;
// instanciación de la capa [dao]
const dao = new Dao(axios);
// complemento [dao]
import pluginDao from './plugins/dao'
Vue.use(pluginDao, dao)
// configuración
Vue.config.productionTip = false
// instanciación del proyecto [App]
new Vue({
render: h => h(App),
}).$mount('#app')
El script [main.js]:
- instancia la capa [dao] en las líneas 14-21;
- integra el complemento [pluginDao] en las líneas 24-25;
- línea 15: se importa la clase [Dao];
- líneas 17-18: se configura el objeto [axios] que realiza las consultas HTTP. Este objeto se importa en la línea 4;
- línea 17: definición de un [timeout] de 2 segundos;
- línea 18: el URL del servidor de cálculo de impuestos;
- línea 19: para poder intercambiar cookies con el servidor;
- líneas 24-25: uso del plugin [pluginDao]
- línea 24: importación del plugin;
- línea 25: integración del complemento. Se observa que el segundo parámetro del método [Vue.use] es la referencia de la capa [dao] definida en la línea 21. Por este motivo, la propiedad [Vue.$dao] designará la capa [dao] en todas las instancias de la clase/función [Vue], es decir, en todos los componentes [Vue.js];
12.5. La vista principal [App.vue]
El código de la vista principal [App] es el siguiente:
<template>
<div class="container">
<b-card>
<!-- mensaje -->
<b-alert show variant="success" align="center">
<h4>[vuejs-10] : plugin [dao], requêtes HTTP asynchrones</h4>
</b-alert>
<!-- componente que realiza una solicitud asíncrona al servidor de cálculo de impuestos-->
<Component1 @error="doSomethingWithError" @endWaiting="endWaiting" @beginWaiting="beginWaiting" />
<!-- visualización de un posible error -->
<b-alert show
variant="danger"
v-if="showError">Evénement [error] intercepté par [App]. Valeur reçue = {{error}}</b-alert>
<!-- mensaje de espera con un indicador de carga -->
<b-alert show v-if="showWaiting" variant="light">
<strong>Requête au serveur de calcul d'impôt en cours...</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",
// estado del componente
data() {
return {
// controla el indicador de espera
showWaiting: false,
// controla la visualización del error
showError: false,
// el error interceptado
error: {}
};
},
// componentes utilizados
components: {
Component1
},
// métodos de gestión de eventos
methods: {
// inicio de la espera
beginWaiting() {
// se muestra la espera
this.showWaiting = true;
// se oculta el mensaje de error
this.showError = false;
},
// fin de la espera
endWaiting() {
// se oculta la espera
this.showWaiting = false;
},
// gestión de errores
doSomethingWithError(error) {
// se indica que se ha producido un error
this.error = error;
// se muestra el mensaje de error
this.showError = true;
}
}
};
</script>
Comentarios
- línea 9: [Component1] es el componente que realiza la consulta asíncrona HTTP. Puede emitir tres eventos:
- [beginWaiting]: la consulta está a punto de realizarse. Se debe mostrar un mensaje de espera al usuario;
- [endWaiting]: la solicitud ha finalizado. Hay que detener la espera;
- [error]: la solicitud ha fallado. Se debe mostrar un mensaje de error;
- líneas 10-13: la alerta que muestra el posible mensaje de error. Está controlada por el booleano [showError] de la línea 33. Muestra el error de la línea 35;
- líneas 14-18: la alerta que muestra el mensaje de espera con un indicador de carga. Está controlada por el valor booleano [showWaiting] de la línea 47;
- líneas 45-50: [beginWaiting] es el método que se ejecuta al recibir el evento [beginWaiting]. Muestra el mensaje de espera (línea 47) y oculta el mensaje de error (línea 49) en caso de que este sea visible tras una operación anterior;
- líneas 52-55: [endWaiting] es el método que se ejecuta al recibir el evento [endWaiting]. Oculta el mensaje de espera (línea 54);
- líneas 57-62: [doSomethingWithError] es el método que se ejecuta al recibir el evento [error]. Registra el error recibido (línea 59) y muestra el mensaje de error (línea 61);
12.6. El componente [Component1]
El código del componente [Component1] es el siguiente:
<template>
<b-row>
<b-col>
<b-alert show
variant="warning"
v-if="showMsg">Valeur reçue du serveur = {{data}}</b-alert>
</b-col>
</b-row>
</template>
<script>
export default {
name: "component1",
// estado del componente
data() {
return {
showMsg: false
};
},
// métodos de gestión de eventos
methods: {
// procesamiento de los datos recibidos del servidor
doSomethingWithData(data) {
// se guardan los datos recibidos
this.data = data;
// se muestran
this.showMsg = true;
}
},
// se acaba de crear el componente
created() {
// se inicializa la sesión con el servidor - solicitud asíncrona
// se utiliza la promesa devuelta por los métodos de la capa [dao]
// se señala el inicio de la operación
this.$emit("beginWaiting");
// se inicia la operación asíncrona
this.$dao
// se trata de inicializar una sesión jSON con el servidor de cálculo de impuestos
.initSession()
// método que procesa los datos recibidos en caso de éxito
.then(data => {
// se procesan los datos recibidos
this.doSomethingWithData(data);
})
// método que procesa el error en caso de error
.catch(error => {
// se reenvía el error al componente principal
this.$emit("error", error.message);
}).finally(() => {
// fin de la espera
this.$emit("endWaiting");
})
}
};
</script>
Comentarios
- líneas 4-6: el componente consta de una única alerta que muestra el valor devuelto por el servidor de cálculo de impuestos, solo en caso de que la consulta HTTP se haya realizado con éxito. Esta alerta está controlada por el valor booleano [showMsg] de la línea 17;
- líneas 31-53: la consulta HTTP se realiza tan pronto como se ha creado el componente. Por lo tanto, se coloca su código en el método [created] de la línea 31;
- línea 35: se indica al componente padre que la consulta asíncrona va a iniciarse;
- líneas 37-39: se ejecuta el método [this.$dao.initSession]. Este inicia una sesión jSON con el servidor de cálculo de impuestos. El resultado inmediato de este método es un [Promise];
- líneas 41-44: este código se ejecuta cuando el servidor ha devuelto su resultado sin errores. El resultado del servidor se encuentra en [data]. En la línea 43, se solicita al método [doSomethingWithData] que procese este resultado;
- líneas 46-49: este código se ejecuta en caso de error durante la ejecución de la consulta. En la línea 48, se indica al componente padre que se ha producido un error y se le pasa el mensaje de error [error.message];
- líneas 49-52: este código se ejecuta en todos los casos. Se indica al componente principal que la solicitud HTTP ha finalizado;
- líneas 23-28: el método [doSomethingWithData] es el encargado de procesar los datos [data] enviados por el servidor. En la línea 25 se registran estos datos y en la línea 27 se muestran;
12.7. Ejecución del proyecto

Si al iniciar el proyecto el servidor de cálculo de impuestos no está en marcha, se obtiene el siguiente resultado:

Iniciemos el servidor [Laragon] (véase https://tahe.developpez.com/tutoriels-cours/php7) y recarguemos la página anterior. El resultado es entonces el siguiente:

Nota: aquí utilizamos la versión 14 del servidor de cálculo de impuestos definida en https://tahe.developpez.com/tutoriels-cours/php7.