12. Projeto [vuejs-10]: plugin [dao], pedidos assíncronos HTTP
A estrutura do projeto [vuejs-10] é a seguinte:

O projeto [vuejs-10] apresenta um componente que efetua uma solicitação HTTP a um servidor remoto. A arquitetura utilizada é a seguinte:

Um componente [Vue.js] utiliza a camada [dao] para comunicar com o servidor de cálculo de impostos.
12.1. Instalação das dependências
A aplicação [vuejs-10] utiliza a biblioteca [axios] para efetuar as consultas assíncronas ao servidor de cálculo de impostos. É necessário instalar esta dependência:

- no [4-5], a linha adicionada ao ficheiro [package.json] após a instalação da biblioteca [axios] [1-3];
12.2. A classe [Dao]
A classe [Dao] é a que foi desenvolvida no parágrafo [La classe Dao]. Reproduzimo-la aqui para referência:
'use strict';
// importações
import qs from 'qs'
// classe [Dao]
class Dao {
// construtor
constructor(axios) {
this.axios = axios;
// cookie de sessão
this.sessionCookieName = "PHPSESSID";
this.sessionCookie = '';
}
// inicialização da sessão
async initSession() {
// opções da solicitação HHTP [get /main.php?action=init-session&type=json]
const options = {
method: "GET",
// parâmetros da URL
params: {
action: 'init-session',
type: 'json'
}
};
// execução da consulta HTTP
return await this.getRemoteData(options);
}
async authentifierUtilisateur(user, password) {
// opções da consulta HHTP [post /main.php?action=authentifier-utilisateur]
const options = {
method: "POST",
headers: {
'Content-type': 'application/x-www-form-urlencoded',
},
// corpo do POST
data: qs.stringify({
user: user,
password: password
}),
// parâmetros do URL
params: {
action: 'authentifier-utilisateur'
}
};
// execução da consulta HTTP
return await this.getRemoteData(options);
}
async getAdminData() {
// opções da consulta HHTP [get /main.php?action=get-admindata]
const options = {
method: "GET",
// parâmetros da consulta URL
params: {
action: 'get-admindata'
}
};
// execução da consulta HTTP
const data = await this.getRemoteData(options);
// resultado
return data;
}
async getRemoteData(options) {
// para o cookie de sessão
if (!options.headers) {
options.headers = {};
}
options.headers.Cookie = this.sessionCookie;
// execução da consulta HTTP
let response;
try {
// solicitação assíncrona
response = await this.axios.request('main.php', options);
} catch (error) {
// o parâmetro [error] é uma instância de exceção — pode assumir várias formas
if (error.response) {
// a resposta do servidor está em [error.response]
response = error.response;
} else {
// o erro é reenviado
throw error;
}
}
// a resposta é o conjunto completo da resposta HTTP do servidor (cabeçalhos HTTP + a própria resposta)
// recupera-se o cookie de sessão, caso exista
const setCookie = response.headers['set-cookie'];
if (setCookie) {
// setCookie é um array
// procura-se o cookie de sessão neste array
let trouvé = false;
let i = 0;
while (!trouvé && i < setCookie.length) {
// procura-se o cookie de sessão
const results = RegExp('^(' + this.sessionCookieName + '.+?);').exec(setCookie[i]);
if (results) {
// guarda-se o cookie de sessão
// eslint-disable-next-line require-atomic-updates
this.sessionCookie = results[1];
// encontrado
trouvé = true;
} else {
// elemento seguinte
i++;
}
}
}
// a resposta do servidor está em [response.data]
return response.data;
}
}
// exportação da classe
export default Dao;
O projeto [vuejs-10] utiliza apenas o método assíncrono [initSession] das linhas 18 a 30. Recorde-se que a classe [Dao] é instanciada com um parâmetro [axios], na linha 10, parâmetro inicializado pelo código chamador. Este código chamador será, neste caso, o script [./main.js].
12.3. O plugin [pluginDao]
O plugin [pluginDao] é o seguinte:
export default {
install(Vue, dao) {
// adiciona uma propriedade [$dao] à classe Vue
Object.defineProperty(Vue.prototype, '$dao', {
// quando Vue.$dao é referenciado, é devolvido o segundo parâmetro [dao]
get: () => dao,
})
}
}
Se nos lembrarmos da explicação dada para o plugin [event-bus], vemos que o plugin [pluginDao] cria, na classe/função [Vue], uma nova propriedade chamada [$dao]. Esta propriedade terá (o que ainda está por demonstrar) como valor o objeto exportado pelo script [./Dao], ou seja, a classe [Dao] anterior.
12.4. O script principal [main.js]
O código do script principal [main.js] é o seguinte:
// importações
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'
// camada [dao]
import Dao from './Dao';
// configuração do Axios
axios.defaults.timeout = 2000;
axios.defaults.baseURL = 'http://localhost/php7/scripts-web/impostos/versão-14';
axios.defaults.withCredentials = true;
// instanciação da camada [dao]
const dao = new Dao(axios);
// plug-in [dao]
import pluginDao from './plugins/dao'
Vue.use(pluginDao, dao)
// configuração
Vue.config.productionTip = false
// instanciação do projeto [App]
new Vue({
render: h => h(App),
}).$mount('#app')
O script [main.js]:
- instancia a camada [dao] nas linhas 14-21;
- integra o plugin [pluginDao] nas linhas 24-25;
- linha 15: a classe [Dao] é importada;
- linhas 17-18: configura-se o objeto [axios], que executa as consultas HTTP. Este objeto é importado na linha 4;
- linha 17: definição de um [timeout] com duração de 2 segundos;
- linha 18: o URL do servidor de cálculo de impostos;
- linha 19: para poder trocar cookies com o servidor;
- linhas 24-25: utilização do plugin [pluginDao]
- linha 24: importação do plugin;
- linha 25: integração do plugin. Vê-se que o segundo parâmetro do método [Vue.use] é a referência da camada [dao] definida na linha 21. É por esta razão que a propriedade [Vue.$dao] designará a camada [dao] em todas as instâncias da classe/função [Vue], ou seja, em todos os componentes [Vue.js];
12.5. A vista principal [App.vue]
O código da vista principal [App] é o seguinte:
<template>
<div class="container">
<b-card>
<!-- mensagem -->
<b-alert show variant="success" align="center">
<h4>[vuejs-10] : plugin [dao], requêtes HTTP asynchrones</h4>
</b-alert>
<!-- componente que efetua uma solicitação assíncrona ao servidor de cálculo de impostos-->
<Component1 @error="doSomethingWithError" @endWaiting="endWaiting" @beginWaiting="beginWaiting" />
<!-- exibição de um eventual erro -->
<b-alert show
variant="danger"
v-if="showError">Evénement [error] intercepté par [App]. Valeur reçue = {{error}}</b-alert>
<!-- mensagem de espera com um indicador de carregamento -->
<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 do componente
data() {
return {
// controla o indicador de espera
showWaiting: false,
// controla a exibição do erro
showError: false,
// erro interceptado
error: {}
};
},
// componentes utilizados
components: {
Component1
},
// métodos de gestão de eventos
methods: {
// início da espera
beginWaiting() {
// exibe-se a espera
this.showWaiting = true;
// oculta-se a mensagem de erro
this.showError = false;
},
// fim da espera
endWaiting() {
// oculta a espera
this.showWaiting = false;
},
// gestão de erros
doSomethingWithError(error) {
// regista-se que ocorreu um erro
this.error = error;
// exibe-se a mensagem de erro
this.showError = true;
}
}
};
</script>
Comentários
- linha 9: [Component1] é o componente que efetua a consulta assíncrona HTTP. Pode emitir três eventos:
- [beginWaiting]: a consulta está prestes a ser efetuada. Deve ser apresentada uma mensagem de espera ao utilizador;
- [endWaiting]: a consulta está concluída. Deve-se interromper a espera;
- [error]: a consulta falhou. Deve ser exibida uma mensagem de erro;
- linhas 10-13: o alerta que exibe a eventual mensagem de erro. É controlado pela variável booleana [showError] da linha 33. Exibe o erro da linha 35;
- linhas 14-18: o alerta que exibe a mensagem de espera com um spinner. É controlado pela variável booleana [showWaiting] da linha 47;
- linhas 45-50: [beginWaiting] é o método executado ao receber o evento [beginWaiting]. Exibe a mensagem de espera (linha 47) e oculta a mensagem de erro (linha 49), caso esta esteja visível na sequência de uma operação anterior;
- linhas 52-55: [endWaiting] é o método executado ao receber o evento [endWaiting]. Oculta a mensagem de espera (linha 54);
- linhas 57-62: [doSomethingWithError] é o método executado ao receber o evento [error]. Regista o erro recebido (linha 59) e apresenta a mensagem de erro (linha 61);
12.6. O componente [Component1]
O código do componente [Component1] é o seguinte:
<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 do componente
data() {
return {
showMsg: false
};
},
// métodos de gestão de eventos
methods: {
// processamento dos dados recebidos do servidor
doSomethingWithData(data) {
// os dados recebidos são gravados
this.data = data;
// exibe-se
this.showMsg = true;
}
},
// o componente acaba de ser criado
created() {
// inicializa-se a sessão com o servidor - pedido assíncrono
// utiliza-se a promessa devolvida pelos métodos da camada [dao]
// sinaliza-se o início da operação
this.$emit("beginWaiting");
// inicia-se a operação assíncrona
this.$dao
// trata-se de inicializar uma sessão jSON com o servidor de cálculo de impostos
.initSession()
// método que processa os dados recebidos em caso de sucesso
.then(data => {
// processam-se os dados recebidos
this.doSomethingWithData(data);
})
// método que trata o erro em caso de falha
.catch(error => {
// o erro é reportado ao componente pai
this.$emit("error", error.message);
}).finally(() => {
// fim da espera
this.$emit("endWaiting");
})
}
};
</script>
Comentários
- linhas 4-6: o componente é constituído por um único alerta que apresenta o valor devolvido pelo servidor de cálculo do imposto, apenas no caso de sucesso da consulta HTTP. Este alerta é controlado pela variável booleana [showMsg] da linha 17;
- linhas 31-53: a consulta HTTP é efetuada assim que o componente é criado. Por isso, o seu código é colocado no método [created] da linha 31;
- linha 35: indica-se ao componente pai que a consulta assíncrona vai iniciar;
- linhas 37-39: o método [this.$dao.initSession] é executado. Este inicializa uma sessão jSON com o servidor de cálculo de impostos. O resultado imediato deste método é um [Promise];
- linhas 41-44: este código é executado quando o servidor devolve o seu resultado sem erros. O resultado do servidor encontra-se em [data]. Na linha 43, solicita-se ao método [doSomethingWithData] que processe este resultado;
- linhas 46-49: este código é executado em caso de erro durante a execução da consulta. Na linha 48, indica-se ao componente pai que ocorreu um erro e passa-se-lhe a mensagem de erro [error.message];
- linhas 49-52: este código é executado em todos os casos. Indica-se ao componente pai que a consulta HTTP está concluída;
- linhas 23-28: o método [doSomethingWithData] é o método encarregado de processar os dados [data] enviados pelo servidor. Na linha 25, registam-se esses dados e, na linha 27, exibem-se;
12.7. Execução do projeto

Se, ao iniciar o projeto, o servidor de cálculo de impostos não estiver em execução, obtém-se o seguinte resultado:

Iniciemos o servidor [Laragon] (ver https://tahe.developpez.com/tutoriels-cours/php7) e recarregue a página acima. O resultado é então o seguinte:

Nota: estamos a utilizar aqui a versão 14 do servidor de cálculo de impostos definida em https://tahe.developpez.com/tutoriels-cours/php7.