Skip to content

12. Projeto [vuejs-10]: plugin [dao], solicitações assíncronas HTTP

A estrutura do projeto [vuejs-10] é a seguinte:

Image

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

Image

Um componente [Vue.js] utiliza a camada [dao] para se 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 realizar consultas assíncronas ao servidor de cálculo de impostos. É necessário instalar essa dependência:

Image

  • no [4-5], a linha adicionada ao arquivo [package.json] após a instalação da biblioteca [axios] [1-3];

12.2. A classe [Dao]

A classe [Dao] é aquela que foi desenvolvida no parágrafo [La classe Dao]. Reproduzimos 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 consulta 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 diversas 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 é uma matriz
      // procura-se o cookie de sessão nessa matriz
      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) {
          // armazenamos o cookie de sessão
          // eslint-disable-next-line require-atomic-updates
          this.sessionCookie = results[1];
          // encontrado
          trouvé = true;
        } else {
          // próximo elemento
          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. Vale lembrar que a classe [Dao] é instanciada com um parâmetro [axios], na linha 10, parâmetro inicializado pelo código chamador. Esse código chamador será, neste caso, o script [./main.js].

12.3. O plugin [pluginDao]

O plug-in [pluginDao] é o seguinte:


export default {
  install(Vue, dao) {
    // adiciona uma propriedade [$dao] à classe Vue
    Object.defineProperty(Vue.prototype, '$dao', {
      // quando Vue.$dao é referenciado, retorna-se o segundo parâmetro [dao]
      get: () => dao,
    })
  }
}

Se nos lembrarmos da explicação dada para o plug-in [event-bus], vemos que o plug-in [pluginDao] cria, na classe/função [Vue], uma nova propriedade chamada [$dao]. Essa propriedade terá (isso ainda precisa ser demonstrado) 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 a 21;
  • integra o plug-in [pluginDao] nas linhas 24-25;
  • linha 15: a classe [Dao] é importada;
  • linhas 17-18: configura-se o objeto [axios], que executa as consultas HTTP. Esse 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 permitir a troca de cookies com o servidor;
  • linhas 24-25: uso do plugin [pluginDao]
    • linha 24: importação do plugin;
    • linha 25: integração do plug-in. Observa-se que o segundo parâmetro do método [Vue.use] é a referência da camada [dao] definida na linha 21. É por esse motivo 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 visualização principal [App.vue]

O código da visão 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 realiza uma solicitação assíncrona ao servidor de cálculo de impostos-->
      <Component1 @error="doSomethingWithError" @endWaiting="endWaiting" @beginWaiting="beginWaiting" />
      <!-- exibição de um possível 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 spinner -->
      <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",
    // status 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 gerenciamento de eventos
    methods: {
      // início da espera
      beginWaiting() {
        // exibição da espera
        this.showWaiting = true;
        // ocultar a mensagem de erro
        this.showError = false;
      },
      // fim da espera
      endWaiting() {
        // oculta a espera
        this.showWaiting = false;
      },
      // gestão de erros
      doSomethingWithError(error) {
        // registra-se que ocorreu um erro
        this.error = error;
        // exibe a mensagem de erro
        this.showError = true;
      }
    }
  };
</script>

Comentários

  • linha 9: [Component1] é o componente que realiza a consulta assíncrona HTTP. Ele pode emitir três eventos:
    • [beginWaiting]: a solicitação será feita. É necessário exibir uma mensagem de espera para o usuário;
    • [endWaiting]: a consulta foi concluída. É preciso encerrar a espera;
    • [error]: a consulta falhou. É necessário exibir uma mensagem de erro;
  • linhas 10-13: o alerta que exibe a eventual mensagem de erro. Ele é controlado pela variável booleana [showError] da linha 33. Ele exibe o erro da linha 35;
  • linhas 14-18: o alerta que exibe a mensagem de espera com um spinner. Ele é controlado pela variável booleana [showWaiting] da linha 47;
  • linhas 45-50: [beginWaiting] é o método executado ao receber o evento [beginWaiting]. Ele exibe a mensagem de espera (linha 47) e oculta a mensagem de erro (linha 49) caso esta esteja visível em decorrência de uma operação anterior;
  • linhas 52-55: [endWaiting] é o método executado ao receber o evento [endWaiting]. Ele oculta a mensagem de espera (linha 54);
  • linhas 57-62: [doSomethingWithError] é o método executado ao receber o evento [error]. Ele registra o erro recebido (linha 59) e exibe 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 gerenciamento de eventos
    methods: {
      // processamento dos dados recebidos do servidor
      doSomethingWithData(data) {
        // os dados recebidos são gravados
        this.data = data;
        // exibindo-os
        this.showMsg = true;
      }
    },
    // o componente acaba de ser criado
    created() {
      // inicializa-se a sessão com o servidor — solicitação assíncrona
      // utiliza-se a promessa retornada 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 => {
          // processa-se os dados recebidos
          this.doSomethingWithData(data);
        })
        // método que processa o erro em caso de falha
        .catch(error => {
          // o erro é repassado 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 exibe o valor retornado pelo servidor de cálculo de impostos, mas somente caso a consulta HTTP seja bem-sucedida. Esse alerta é controlado pela variável booleana [showMsg] da linha 17;
  • linhas 31-53: a consulta HTTP é executada assim que o componente é criado. Portanto, seu código é colocado no método [created] da linha 31;
  • linha 35: informa-se ao componente pai que a consulta assíncrona será iniciada;
  • linhas 37-39: o método [this.$dao.initSession] é executado. Ele inicializa uma sessão jSON com o servidor de cálculo de impostos. O resultado imediato desse método é um [Promise];
  • linhas 41-44: esse código é executado quando o servidor retorna seu resultado sem erros. O resultado do servidor está em [data]. Na linha 43, solicita-se ao método [doSomethingWithData] que processe esse resultado;
  • linhas 46-49: este código é executado em caso de erro durante a execução da consulta. Na linha 48, informa-se ao componente pai que ocorreu um erro e passa-se a ele a mensagem de erro [error.message];
  • linhas 49-52: esse código é executado em todos os casos. Indica-se ao componente pai que a solicitação HTTP foi concluída;
  • linhas 23-28: o método [doSomethingWithData] é responsável por processar os dados [data] enviados pelo servidor. Na linha 25, esses dados são gravados e, na linha 27, são exibidos;

12.7. Execução do projeto

Image

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

Image

Vamos iniciar o servidor [Laragon] (consulte https://tahe.developpez.com/tutoriels-cours/php7) e recarregar a página acima. O resultado será então o seguinte:

Image

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