Skip to content

12. Progetto [vuejs-10]: plugin [dao], richieste asincrone HTTP

La struttura del progetto [vuejs-10] è la seguente:

Image

Il progetto [vuejs-10] mostra un componente che effettua una richiesta HTTP a un server remoto. L’architettura utilizzata è la seguente:

Image

Un componente [Vue.js] utilizza il livello [dao] per comunicare con il server di calcolo delle imposte.

12.1. Installazione delle dipendenze

L’applicazione [vuejs-10] utilizza la libreria [axios] per effettuare le richieste asincrone al server di calcolo delle imposte. È necessario installare questa dipendenza:

Image

  • in [4-5], la riga aggiunta al file [package.json] dopo l’installazione della libreria [axios] [1-3] ;

12.2. La classe [Dao]

La classe [Dao] è quella sviluppata nel paragrafo [La classe Dao]. La riportiamo qui di seguito a titolo di promemoria:


'use strict';

// importazioni
import qs from 'qs'

// classe [Dao]
class Dao {

  // costruttore
  constructor(axios) {
    this.axios = axios;
    // cookie di sessione
    this.sessionCookieName = "PHPSESSID";
    this.sessionCookie = '';
  }

  // inizializzazione sessione
  async  initSession() {
    // opzioni della richiesta HHTP [get /main.php?action=init-session&type=json]
    const options = {
      method: "GET",
      // parametri di URL
      params: {
        action: 'init-session',
        type: 'json'
      }
    };
    // esecuzione della query HTTP
    return await this.getRemoteData(options);
  }

  async  authentifierUtilisateur(user, password) {
    // opzioni della richiesta HHTP [post /main.php?action=authentifier-utilisateur]
    const options = {
      method: "POST",
      headers: {
        'Content-type': 'application/x-www-form-urlencoded',
      },
      // corpo del POST
      data: qs.stringify({
        user: user,
        password: password
      }),
      // parametri dell'URL
      params: {
        action: 'authentifier-utilisateur'
      }
    };
    // esecuzione della richiesta HTTP
    return await this.getRemoteData(options);
  }

  async getAdminData() {
    // opzioni della richiesta HHTP  [get /main.php?action=get-admindata]
    const options = {
      method: "GET",
      // parametri di URL
      params: {
        action: 'get-admindata'
      }
    };
    // esecuzione della query HTTP
    const data = await this.getRemoteData(options);
    // risultato
    return data;
  }

  async  getRemoteData(options) {
    // per il cookie di sessione
    if (!options.headers) {
      options.headers = {};
    }
    options.headers.Cookie = this.sessionCookie;
    // esecuzione della richiesta HTTP
    let response;
    try {
      // richiesta asincrona
      response = await this.axios.request('main.php', options);
    } catch (error) {
      // il parametro [error] è un'istanza di eccezione - può assumere diverse forme
      if (error.response) {
        // la risposta del server è contenuta in [error.response]
        response = error.response;
      } else {
        // l'errore viene generato nuovamente
        throw error;
      }
    }
    // la risposta è l'insieme della risposta HTTP del server (intestazioni HTTP + la risposta stessa)
    // si recupera il cookie di sessione, se presente
    const setCookie = response.headers['set-cookie'];
    if (setCookie) {
      // setCookie è un array
      // si cerca il cookie di sessione in questo array
      let trouvé = false;
      let i = 0;
      while (!trouvé && i < setCookie.length) {
        // si cerca il cookie di sessione
        const results = RegExp('^(' + this.sessionCookieName + '.+?);').exec(setCookie[i]);
        if (results) {
          // si memorizza il cookie di sessione
          // eslint-disable-next-line require-atomic-updates
          this.sessionCookie = results[1];
          // trovato
          trouvé = true;
        } else {
          // elemento successivo
          i++;
        }
      }
    }
    // la risposta del server è in [response.data]
    return response.data;
  }
}

// esportazione della classe
export default Dao;

Il progetto [vuejs-10] utilizza esclusivamente il metodo asincrono [initSession] delle righe 18-30. Si ricorda che la classe [Dao] viene istanziata con un parametro [axios], riga 10, inizializzato dal codice chiamante. Tale codice chiamante sarà in questo caso lo script [./main.js].

12.3. Il plugin [pluginDao]

Il plugin [pluginDao] è il seguente:


export default {
  install(Vue, dao) {
    // aggiunge una proprietà [$dao] alla classe Vue
    Object.defineProperty(Vue.prototype, '$dao', {
      // quando viene richiamata Vue.$dao, viene restituito il secondo parametro [dao]
      get: () => dao,
    })
  }
}

Se si ripensa alla spiegazione fornita per il plugin [event-bus], si nota che il plugin [pluginDao] crea, nella classe/funzione [Vue], una nuova proprietà denominata [$dao]. Questa proprietà avrà (resta da dimostrare) come valore l’oggetto esportato dallo script [./Dao], ovvero la precedente classe [Dao].

12.4. Lo script principale [main.js]

Il codice dello script principale [main.js] è il seguente:


// importazioni
import Vue from 'vue'
import App from './App.vue'
import axios from 'axios';

// plugin
import BootstrapVue from 'bootstrap-vue'
Vue.use(BootstrapVue);

// bootstrap
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'

// livello [dao]
import Dao from './Dao';
// configurazione axios
axios.defaults.timeout = 2000;
axios.defaults.baseURL = 'http://localhost/php7/scripts-web/impots/versione-14';
axios.defaults.withCredentials = true;
// istanza del livello [dao]
const dao = new Dao(axios);

// plugin [dao]
import pluginDao from './plugins/dao'
Vue.use(pluginDao, dao)

// configurazione
Vue.config.productionTip = false

// istanza del progetto [App]
new Vue({
  render: h => h(App),
}).$mount('#app')

Lo script [main.js]:

  • istanzia il livello [dao] alle righe 14-21;
  • integra il plugin [pluginDao] alle righe 24-25;
  • riga 15: viene importata la classe [Dao];
  • righe 17-18: si configura l’oggetto [axios] che esegue le richieste HTTP. Questo oggetto viene importato alla riga 4;
    • riga 17: definizione di un [timeout] della durata di 2 secondi;
    • riga 18: l’oggetto URL del server di calcolo delle imposte;
    • riga 19: per poter scambiare i cookie con il server;
  • righe 24-25: utilizzo del plugin [pluginDao]
    • riga 24: importazione del plugin;
    • riga 25: integrazione del plugin. Si nota che il secondo parametro del metodo [Vue.use] è il riferimento al livello [dao] definito alla riga 21. È per questo motivo che la proprietà [Vue.$dao] indicherà il livello [dao] in tutte le istanze della classe/funzione [Vue], ovvero in tutti i componenti [Vue.js];

12.5. La vista principale [App.vue]

Il codice della vista principale [App] è il seguente:


<template>
  <div class="container">
    <b-card>
      <!-- messaggio -->
      <b-alert show variant="success" align="center">
        <h4>[vuejs-10] : plugin [dao], requêtes HTTP asynchrones</h4>
      </b-alert>
      <!-- componente che effettua una richiesta asincrona al server di calcolo delle imposte-->
      <Component1 @error="doSomethingWithError" @endWaiting="endWaiting" @beginWaiting="beginWaiting" />
      <!-- visualizzazione di un eventuale errore -->
      <b-alert show
               variant="danger"
               v-if="showError">Evénement [error] intercepté par [App]. Valeur reçue = {{error}}</b-alert>
      <!-- messaggio di attesa con un indicatore di caricamento -->
      <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",
    // stato del componente
    data() {
      return {
        // controllo dell'indicatore di caricamento
        showWaiting: false,
        // controllo della visualizzazione dell'errore
        showError: false,
        // errore intercettato
        error: {}
      };
    },
    // componenti utilizzati
    components: {
      Component1
    },
    // metodi di gestione degli eventi
    methods: {
      // inizio attesa
      beginWaiting() {
        // viene visualizzata l'attesa
        this.showWaiting = true;
        // si nasconde il messaggio di errore
        this.showError = false;
      },
      // fine attesa
      endWaiting() {
        // si nasconde l'attesa
        this.showWaiting = false;
      },
      // gestione dell'errore
      doSomethingWithError(error) {
        // si rileva che si è verificato un errore
        this.error = error;
        // visualizzazione del messaggio di errore
        this.showError = true;
      }
    }
  };
</script>

Commenti

  • riga 9: [Component1] è il componente che esegue la richiesta asincrona HTTP. Può generare tre eventi:
    • [beginWaiting]: la richiesta sta per essere effettuata. È necessario visualizzare un messaggio di attesa per l’utente;
    • [endWaiting]: la richiesta è terminata. È necessario interrompere l’attesa;
    • [error]: la richiesta non è andata a buon fine. È necessario visualizzare un messaggio di errore;
  • righe 10-13: l'avviso che visualizza l'eventuale messaggio di errore. È controllato dal valore booleano [showError] della riga 33. Visualizza l'errore della riga 35;
  • righe 14-18: l’avviso che visualizza il messaggio di attesa con uno spinner. È controllato dalla variabile booleana [showWaiting] alla riga 47;
  • righe 45-50: [beginWaiting] è il metodo eseguito alla ricezione dell’evento [beginWaiting]. Visualizza il messaggio di attesa (riga 47) e nasconde il messaggio di errore (riga 49) nel caso in cui quest’ultimo fosse visibile a seguito di un’operazione precedente;
  • righe 52-55: [endWaiting] è il metodo eseguito alla ricezione dell’evento [endWaiting]. Nasconde il messaggio di attesa (riga 54);
  • righe 57-62: [doSomethingWithError] è il metodo eseguito alla ricezione dell’evento [error]. Registra l’errore ricevuto (riga 59) e visualizza il messaggio di errore (riga 61);

12.6. Il componente [Component1]

Il codice del componente [Component1] è il seguente:


<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",
    // stato del componente
    data() {
      return {
        showMsg: false
      };
    },
    // metodi di gestione degli eventi
    methods: {
      // elaborazione dei dati ricevuti dal server
      doSomethingWithData(data) {
        // si registrano i dati ricevuti
        this.data = data;
        // vengono visualizzati
        this.showMsg = true;
      }
    },
    // il componente è stato appena creato
    created() {
      // si inizializza la sessione con il server - richiesta asincrona
      // si utilizza la promessa restituita dai metodi del livello [dao]
      // si segnala l'inizio dell'operazione
      this.$emit("beginWaiting");
      // si avvia l'operazione asincrona
      this.$dao
        // si tratta di inizializzare una sessione jSON con il server di calcolo delle imposte
        .initSession()
        // metodo che elabora i dati ricevuti in caso di esito positivo
        .then(data => {
          // si elaborano i dati ricevuti
          this.doSomethingWithData(data);
        })
        // metodo che gestisce l'errore in caso di errore
        .catch(error => {
          // si segnala l'errore al componente padre
          this.$emit("error", error.message);
        }).finally(() => {
          // fine dell'attesa
          this.$emit("endWaiting");
        })
    }
  };
</script>

Commenti

  • righe 4-6: il componente è costituito da un unico avviso che visualizza il valore restituito dal server di calcolo delle imposte, ma solo in caso di esito positivo della richiesta HTTP. Questo avviso è controllato dal valore booleano [showMsg] alla riga 17;
  • righe 31-53: la richiesta HTTP viene effettuata non appena il componente è stato creato. Il relativo codice viene quindi inserito nel metodo [created] alla riga 31;
  • riga 35: si comunica al componente padre che la richiesta asincrona sta per iniziare;
  • righe 37-39: viene eseguito il metodo [this.$dao.initSession]. Esso inizializza una sessione jSON con il server di calcolo delle imposte. Il risultato immediato di questo metodo è un [Promise];
  • righe 41-44: questo codice viene eseguito quando il server ha restituito il risultato senza errori. Il risultato del server si trova in [data]. Alla riga 43, si richiede al metodo [doSomethingWithData] di elaborare tale risultato;
  • righe 46-49: questo codice viene eseguito in caso di errore durante l’esecuzione della richiesta. Alla riga 48, si comunica al componente padre che si è verificato un errore e gli viene passato il messaggio di errore [error.message];
  • righe 49-52: questo codice viene eseguito in ogni caso. Si comunica al componente padre che la richiesta HTTP è terminata;
  • righe 23-28: il metodo [doSomethingWithData] è incaricato di elaborare i dati [data] inviati dal server. Alla riga 25 si salvano questi dati e alla riga 27 li si visualizza;

12.7. Esecuzione del progetto

Image

Se, all’avvio del progetto, il server di calcolo delle imposte non è in esecuzione, si ottiene il seguente risultato:

Image

Avviamo il server [Laragon] (cfr. https://tahe.developpez.com/tutoriels-cours/php7) e ricarichiamo la pagina sopra riportata. Il risultato è quindi il seguente:

Image

Nota: in questo caso utilizziamo la versione 14 del server di calcolo delle imposte definita all’indirizzo https://tahe.developpez.com/tutoriels-cours/php7.