IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Introduction au framework VUE.JS par l'exemple


précédentsommairesuivant

XIII. projet [vuejs-10] : plugin [dao], requêtes HTTP asynchrones

L’arborescence du projet [vuejs-10] est la suivante :

Image non disponible

Le projet [vuejs-10] montre un composant faisant une requête HTTP à un serveur distant. L’architecture utilisée est la suivante :

Image non disponible

Un composant [Vue.js] utilise la couche [dao] pour dialoguer avec le serveur de calcul de l’impôt.

XIII-A. Installation des dépendances

L’application [vuejs-10] utilise la bibliothèque [axios] pour faire les requêtes asynchrones vers le serveur de calcul d’impôt. Il nous faut installer cette dépendance :

Image non disponible
  • en [4-5], la ligne ajoutée au fichier [package.json] après l’installation de la bibliothèque [axios] [1-3] ;

XIII-B. La classe [Dao]

La classe [Dao] est celle qui a été développée dans le document |Introduction au langage ECMASCRIPT 6 par l’exemple|. Nous la redonnons ici pour mémoire :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
'use strict';

// imports
import qs from 'qs'

// classe [Dao]
class Dao {

  // constructeur
  constructor(axios) {
    this.axios = axios;
    // cookie de session
    this.sessionCookieName = "PHPSESSID";
    this.sessionCookie = '';
  }

  // init session
  async initSession() {
    // options de la requête HHTP [get /main.php?action=init-session&type=json]
    const options = {
      method: "GET",
      // paramètres de l'URL
      params: {
        action: 'init-session',
        type: 'json'
      }
    };
    // exécution de la requête HTTP
    return await this.getRemoteData(options);
  }

  async authentifierUtilisateur(user, password) {
    // options de la requête HHTP [post /main.php?action=authentifier-utilisateur]
    const options = {
      method: "POST",
      headers: {
        'Content-type': 'application/x-www-form-urlencoded',
      },
      // corps du POST
      data: qs.stringify({
        user: user,
        password: password
      }),
      // paramètres de l'URL
      params: {
        action: 'authentifier-utilisateur'
      }
    };
    // exécution de la requête HTTP
    return await this.getRemoteData(options);
  }

  async getAdminData() {
    // options de la requête HHTP  [get /main.php?action=get-admindata]
    const options = {
      method: "GET",
      // paramètres de l'URL
      params: {
        action: 'get-admindata'
      }
    };
    // exécution de la requête HTTP
    const data = await this.getRemoteData(options);
    // résultat
    return data;
  }

  async getRemoteData(options) {
    // pour le cookie de session
    if (!options.headers) {
      options.headers = {};
    }
    options.headers.Cookie = this.sessionCookie;
    // exécution de la requête HTTP
    let response;
    try {
      // requête asynchrone
      response = await this.axios.request('main.php', options);
    } catch (error) {
      // le paramètre [error] est une instance d'exception - elle peut avoir diverses formes
      if (error.response) {
        // la réponse du serveur est dans [error.response]
        response = error.response;
      } else {
        // on relance l'erreur
        throw error;
      }
    }
    // response est l'ensemble de la réponse HTTP du serveur (entêtes HTTP + réponse elle-même)
    // on récupère le cookie de session s'il existe
    const setCookie = response.headers['set-cookie'];
    if (setCookie) {
      // setCookie est un tableau
      // on cherche le cookie de session dans ce tableau
      let trouvé = false;
      let i = 0;
      while (!trouvé && i < setCookie.length) {
        // on cherche le cookie de session
        const results = RegExp('^(' + this.sessionCookieName + '.+?);').exec(setCookie[i]);
        if (results) {
          // on mémorise le cookie de session
          // eslint-disable-next-line require-atomic-updates
          this.sessionCookie = results[1];
          // on a trouvé
          trouvé = true;
        } else {
          // élément suivant
          i++;
        }
      }
    }
    // la réponse du serveur est dans [response.data]
    return response.data;
  }
}

// export de la classe
export default Dao;

Le projet [vuejs-10] n’utilise que la méthode asynchrone [initSession] des lignes 18-30. On rappelle que la classe [Dao] est instanciée avec un paramètre [axios], ligne 10, paramètre initialisé par le code appelant. Ce code appelant sera ici le script [./main.js].

XIII-C. Le plugin [pluginDao]

Le plugin [pluginDao] est le suivant :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
export default {
  install(Vue, dao) {
    // ajoute une propriété [$dao] à la classe Vue
    Object.defineProperty(Vue.prototype, '$dao', {
      // lorsque Vue.$dao est référencé, on rend le 2ième paramètre [dao]
      get: () => dao,
    })
  }
}

Si on se souvient de l’explication donnée pour le plugin [event-bus], on voit que le plugin [pluginDao] crée dans la classe / fonction [Vue], une nouvelle propriété appelée [$dao]. Cette propriété aura (ça reste à montrer) pour valeur, l’objet exporté par le script [./Dao], ç-à-d la classe [Dao] précédente.

XIII-D. Le script principal [main.js]

Le code du script principal [main.js] est le suivant :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
// 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'

// couche [dao]
import Dao from './Dao';
// configuration axios
axios.defaults.timeout = 2000;
axios.defaults.baseURL = 'http://localhost/php7/scripts-web/impots/version-14';
axios.defaults.withCredentials = true;
// instanciation couche [dao]
const dao = new Dao(axios);

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

// configuration
Vue.config.productionTip = false

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

Le script [main.js] :

  • instancie la couche [dao] aux lignes 14-21 ;
  • intègre le plugin [pluginDao] aux lignes 24-25 ;
  • ligne 15 : la classe [Dao] est importée ;
  • lignes 17-18 : on configure l’objet [axios] qui réalise les requêtes HTTP. Cet objet est importé à la ligne 4 ;

    • ligne 17 : définition d’un [timeout] de 2 secondes ;
    • ligne 18 : l’URL du serveur de calcul de l’impôt ;
    • ligne 19 : pour pouvoir échanger des cookies avec le serveur ;
  • lignes 24-25 : utilisation du plugin [pluginDao]

    • ligne 24 : import du plugin ;
    • ligne 25 : intégration du plugin. On voit que le second paramètre de la méthode [Vue.use] est la référence de la couche [dao] définie ligne 21. C’est pour cette raison que la propriété [Vue.$dao] désignera la couche [dao] dans toutes les instances de la classe / fonction [Vue], ç-à-d dans tous les composants [Vue.js] ;

XIII-E. La vue principale [App.vue]

Le code de la vue principale [App] est le suivant :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
<template>
  <div class="container">
    <b-card>
      <!-- message -->
      <b-alert show variant="success" align="center">
        <h4>[vuejs-10] : plugin [dao], requêtes HTTP asynchrones</h4>
      </b-alert>
      <!-- composant faisant une requête asynchrone au serveur de calcul d'impôt-->
      <Component1 @error="doSomethingWithError" @endWaiting="endWaiting" @beginWaiting="beginWaiting" />
      <!-- affichage d'une éventuelle erreur -->
      <b-alert show
               variant="danger"
               v-if="showError">Evénement [error] intercepté par [App]. Valeur reçue = {{error}}</b-alert>
      <!-- message d'attente avec un 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",
    // état du composant
    data() {
      return {
        // contrôle le spinner d'attente
        showWaiting: false,
        // contrôle l'affichage de l'erreur
        showError: false,
        // l'erreur interceptée
        error: {}
      };
    },
    // composants utilisés
    components: {
      Component1
    },
    // méthodes de gestion des évts
    methods: {
      // début attente
      beginWaiting() {
        // on affiche l'attente
        this.showWaiting = true;
        // on cache le msg d'erreur
        this.showError = false;
      },
      // fin attente
      endWaiting() {
        // on cache l'attente
        this.showWaiting = false;
      },
      // gestion d'erreur
      doSomethingWithError(error) {
        // on note qu'il y a eu erreur
        this.error = error;
        // on affiche le msg d'erreur
        this.showError = true;
      }
    }
  };
</script>

Commentaires

  • ligne 9 : [Component1] est le composant qui fait la requête HTTP asynchrone. Il peut émettre trois événements :

    • [beginWaiting] : la requête va être faite. Il faut afficher un message d’attente à destination de l’utilisateur ;
    • [endWaiting] : la requête est terminée. Il faut arrêter l’attente ;
    • [error] : la requête s’est mal passée. Il faut afficher un message d’erreur ;
  • lignes 10-13 : l’alerte qui affiche l’éventuel message d’erreur. Elle est contrôlée par le booléen [showError] de la ligne 33. Elle affiche l’erreur de la ligne 35 ;
  • lignes 14-18 : l’alerte qui affiche le message d’attente avec un spinner. Elle est contrôlée par le booléen [showWaiting] de la ligne 47 ;
  • lignes 45-50 : [beginWaiting] est la méthode exécutée à réception de l’événement [beginWaiting]. Elle affiche le message d’attente (ligne 47) et cache le message d’erreur (ligne 49) au cas où celui-ci serait visible suite à une opération précédente ;
  • lignes 52-55 : [endWaiting] est la méthode exécutée à réception de l’événement [endWaiting]. Elle cache le message d’attente (ligne 54) ;
  • lignes 57-62 : [doSomethingWithError] est la méthode exécutée à réception de l’événement [error]. Elle enregistre l’erreur reçue (ligne 59) et affiche le message d’erreur (ligne 61) ;

XIII-F. Le composant [Component1]

Le code du composant [Component1] est le suivant :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
<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",
    // état du composant
    data() {
      return {
        showMsg: false
      };
    },
    // méthodes de gestion des évts
    methods: {
      // traitement de la donnée reçue du serveur
      doSomethingWithData(data) {
        // on enregistre la donnée reçue
        this.data = data;
        // on l'affiche
        this.showMsg = true;
      }
    },
    // le composant vient d'être créé
    created() {
      // on initialise la session avec le serveur - requête asynchrone
      // on utilise la promesse rendue par les méthodes de la couche [dao]
      // on signale le début de l'opération
      this.$emit("beginWaiting");
      // on lance l'opération asynchrone
      this.$dao
        // il s'agit d'initialiser une session jSON avec le serveur de calcul de l'impôt
        .initSession()
        // méthode qui traite la donnée reçue en cas de succès
        .then(data => {
          // on traite la donnée reçue
          this.doSomethingWithData(data);
        })
        // méthode qui traite l'erreur en cas d'erreur
        .catch(error => {
          // on remonte l'erreur au composant parent
          this.$emit("error", error.message);
        }).finally(() => {
          // fin de l'attente
          this.$emit("endWaiting");
        })
    }
  };
</script>

Commentaires

  • lignes 4-6 : le composant est constitué d’une unique alerte qui affiche la valeur renvoyée par le serveur de calcul de l’impôt, ceci uniquement en cas de succès de la requête HTTP. Cette alerte est contrôlée par le booléen [showMsg] de la ligne 17 ;
  • lignes 31-53 : la requête HTTP est faite dès que le composant a été créé. On met donc son code dans la méthode [created] de la ligne 31 ;
  • ligne 35 : on indique au composant parent que la requête asynchrone va démarrer ;
  • lignes 37-39 : la méthode [this.$dao.initSession] est exécutée. Elle initialise une session jSON avec le serveur de calcul d’impôt. Le résultat immédiat de cette méthode est une [Promise] ;
  • lignes 41-44 : ce code s’exécute lorsque le serveur a rendu son résultat sans erreur. Le résultat du serveur est dans [data]. Ligne 43, on demande à la méthode [doSomethingWithData] de traiter ce résultat ;
  • lignes 46-49 : ce code s’exécute en cas d’erreur lors de l’exécution de la requête. Ligne 48, on indique au composant parent qu’une erreur est survenue et on lui passe le message de l’erreur [error.message] ;
  • lignes 49-52 : ce code s’exécute dans tous les cas. On indique au composant parent que la requête HTTP est terminée ;
  • lignes 23-28 : la méthode [doSomethingWithData] est la méthode chagée d’exploiter la donnée [data] envoyée par le serveur. Ligne 25, on enregistre cette donnée et ligne 27 on l’affiche ;

XIII-G. Exécution du projet

Image non disponible

Si lorsqu’on lance le projet, le serveur de calcul d’impôt n’est pas lancé alors on obtient le résultat suivant :

Image non disponible

Lançons le serveur [Laragon] (cf https://tahe.developpez.com/tutoriels-cours/php7) et rechargeons la page ci-dessus. Le résultat est alors le suivant :

Image non disponible

Note : nous utilisons ici la version 14 du serveur de calcul d’impôt définie au https://tahe.developpez.com/tutoriels-cours/php7.


précédentsommairesuivant

Licence Creative Commons
Le contenu de cet article est rédigé par Serge Tahé et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 3.0 non transposé.
Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright © 2019 Developpez.com.