Grégory Bourguin
SysReIC - LISIC - ULCO
Vues.js - Extras

Axios

Axios ne fait pas partie de Vue.js, mais ce framework est souvent utilisé dans ce contexte pour consommer des API dont les résultats sont injectés dans des composants Vue.

La documentation Axios est disponible sur https://github.com/axios/axios.

Axios est basée sur le concept de Promesse.

Démarrage

Installation

L'installation d'Axios dans un projet Vue est faite grâce à nmp

npm install axios

Importation

Pour utiliser Axios dans un script, il faut l'importer :

<script>
...
import axios from 'axios' ;
...
</script>

Utilisation

L'application suivante présente des exemples d'utilisation d'Axios pour consommer des API REST qui fournissent du JSON.

Les boutons Get new Joke et Get new Advice déclenchent respectivement les méthodes newJoke() et newAdvice() qui utilisent Axios pour consommer des API (résultat en JSON).

En utilisant le mécanisme de Promesse fourni par Axios, les résultats des requêtes sont stockés dans des listes de jokes/advices. Ces listes sont affichées grâces à un classique v-for.

Chaque joke/advice est quant à lui affiché grâce à un composant simple nommé Strip.

smpl_08_App_axios.vue : 2 exemples d'utilisation d'Axios (méthode GET).
<template>
<div id="app">

  <div class="strips">
    <b-btn @click="newJoke">Get new Joke</b-btn>
    <strip v-for="j in jokes" :key="j.id" :text="j.joke"></strip>
  </div>

  <div class="strips">
    <b-btn @click="newAdvice">Get new Advice</b-btn>
    <strip v-for="a in advices" :key="a.slip_id" :text="a.advice"></strip>
  </div>

</div>
</template>

<script>
import axios from "axios";

import smpl_08_Strip from "@/components/samples/smpl_08_axios/smpl_08_Strip";

const jokeOptions = {
  method: 'GET',
  url: 'https://icanhazdadjoke.com/', // https://icanhazdadjoke.com/api
  headers: {
    'Accept': 'application/json',
  }
};

export default {
  name: "App",
  components:{
    strip: smpl_08_Strip
  },
  data(){ return {
    jokes: [],
    advices: [],
  }},
  methods:{

    newJoke(){
      axios.request(jokeOptions).
      then(response => {
        this.jokes.push(response.data)
      }).
      catch(error => {
        console.error(error);
      });
    },

    newAdvice(){
      axios.get(`https://api.adviceslip.com/advice`). //https://api.adviceslip.com/
      then(response => {
        console.log(response)
        this.advices.push(response.data.slip)
      }).
      catch(error => {
        console.error(error);
      });
    },
  }
}
</script>

<style scoped>

#app{
  display: flex;
  flex-direction: row;
  justify-items: stretch;
}

.strips{
  flex: 1 ;
  margin: 10px;

  display: flex;
  flex-direction: column;

}
.strip{
  margin: 5px 0px;
  background: whitesmoke;
}
</style>
smpl_08_Strip.vue : un simple composant d'affichage de texte.
<template>
  <div class="mdl-shadow--3dp strip">
    {{ text }}
  </div>
</template>

<script>
export default {
  name: "smpl_08_Strip",
  props:{
    text: String
  }
}
</script>

<style scoped>
/*https://fonts.google.com/*/
@import url('https://fonts.googleapis.com/css2?family=Architects+Daughter&display=swap');

.strip{
  padding: 5px;

  font-family: 'Architects Daughter', cursive;
  font-size: 1.3em;
}
</style>
Résultat.

NB: il est possible de faire bien plus de choses avec Axios (autres méthodes, paramètres, annulation, etc.). Vous pourrez trouver d'autres exemples (sans Vue) dans la documentation officielle.

TP 04

En utilisant l'API https://restcountries.com/, réalisez l'application Vue ci-dessous en prêtant bien attention aux détails visuels ET fonctionnels.

Quelques remarques à prendre en compte :

  • La liste des régions est construite dynamiquement à partir des informations fournies par https://restcountries.com/.
  • La liste des régions est dans l'ordre alphabétique.
  • Quand le nom d'un pays est trop grand pour le composant Country, il est tronqué avec des "..." (voir CSS ellipsis).
  • Lorsqu'un pays a plusieurs time zones (ex. la France), "Time Zones" prend un 's' (NB: il n'y a pas de 's' quand il n'y a qu'une zone), et les UTC sont séparés par des virgules. Par contre, il n'y a pas de virgule après le dernier UTC.
  • Le passage en mode recherche (click sur la loupe) donne le focus à l'input "County name...").
  • En mode recherche, l'action est déclenchée par un click sur "Search" ou par un appui sur la touche "Entrée".
  • Lors de la fermeture du mode recherche (click sur la croix), la région sélectionnée est celle qui l'était avant de passer en mode recherche.
Résultat attendu.
--> Attention à la mise en forme <--

TP 05

En utilisant l'API icanhazdadjoke, réalisez l'application Vue ci-dessous en prêtant bien attention aux détails visuels ET fonctionnels.

Quelques remarques à prendre en compte :

  • Au départ, la zone de résultats est vide.
  • L'application fournit (au maximum) 5 blagues par page, et permet de lister toutes les blagues correspondant à une recherche grâce aux boutons "prev" et "next".
  • Le numéro de la page courante et le nombre de pages total sont affichés.
  • Les boutons "prev" et "next" sont désactivés quand ils ne peuvent (doivent) pas être utilisés.
  • La liste des mots clés correspondant à la page affichée est rappelée entre guillemets sous la liste des blagues.
  • Une recherche avec mot(s) clé(s) qui n'existe(nt) pas affiche le message "Pas de résultat !".
Résultat attendu.
--> Attention à la mise en forme <--

Vue Router

Vue Router permet aux application Vue de mettre en place une solution de routage qui remplace (une partie) des composants de la page en fonction de l'adresse indiquée dans le navigateur.

Installation

Pour faire fonctionner Vue Router dans un projet il faut tout d'abord installer le package vue-router.

npm install vue-router

Déclarations

Les composants à intégrer

Le but du router étant de charger des composants au sein de l'application en fonction de l'URL visée dans la barre d'adresse du navigateur, il faut dans un premier temps créer ces composants.

NB : les composants chargés sont des composants Vue "classiques" tels que nous les avons créés dans les parties précédentes de ce cours.

Nous allons ici prendre pour exemple 2 composants très simples nommés Blue.vue et Red.vue chargés d'afficher chacun un carré de couleur.

Pour simplifier l'exemple, le code de ces 2 composants est ici très similaire...

Blue.vue
<template>
<div id="blue-comp" class="square">
  Blue
</div>
</template>

<script>
    export default {
        name: "Blue"
    }
</script>

<style scoped>
  @import "square.css";
  #blue-comp{
    background: lightskyblue;
  }
</style>
Red.vue
<template>
<div id="red-comp" class="square">
  Red
</div>
</template>

<script>
    export default {
        name: "Red"
    }
</script>

<style scoped>
  @import "square.css";
  #red-comp{
    background: lightpink;
  }
</style>
square.css
.square{
    width: 300px;
    height: 300px;
    margin: auto;
    border: 1px solid black;
    background: lightskyblue;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 2em;
}

Importation, instanciation et configuration du router

L'étape suivante consiste à importer, instancier et configurer le router dans le script de l'application.

Le script de App.vue
<script>
    // importation des composants qui seront intégrés dynamiquement
    import Blue from "@/components/Blue";
    import Red from "@/components/Red";

    // importation des packages pour faire fonctionner VueRouter
    import Vue from 'vue' ;
    import VueRouter from 'vue-router' ;
    
    // DÉCLARATION DES ROUTES (un mapping URL -> composant à charger)
    const routes = [
      { path: '/blue', component: Blue },
      { path: '/red', component: Red },
    ]
    
    // instanciation du router
    const router = new VueRouter({
      routes // short for `routes: routes`
    })
    Vue.use(VueRouter) ;
    
    export default {
      name: 'App',
      router // déclaration du router dans l'application
    }
</script>

Insertion dans le template : router-view

Il ne reste plus qu'à indiquer l'endroit où seront chargés les composants et éventuellement mettre des liens qui permettent de passer de l'un à l'autre.

On utilise la balise router-view pour indiquer où seront chargés les composants: cette balise sera remplacée par le composant chargé par le router en fonction de l'URL donnée au navigateur.

Le template de App.vue
<template>
  <div id="app">
    <h1>Router Application</h1>
    
    <router-view></router-view>
    
  </div>
</template>

Si on éxécute l'application comme nous l'avons fait jusqu'à présent (npm run serve), l'adresse http://localhost:8080/#/ affiche simplement le titre "Router Application".

En ajoutant une des routes définies dans le script (ex. http://localhost:8080/#/blue), le composant mappé sur cette URL est chargé dynamiquement à l'endroit du router-view.

Ajout de liens : router-link

On peut utiliser la balise router-link pour créer des liens aisément compréhensibles par le router : ces balises utilisent les routes qui ont été définies dans la partie script de l'application.

Le template de App.vue (avec des liens)
<template>
  <div id="app">
    <h1>Router Application</h1>
    
    <nav>
      <router-link to="/blue">Blue</router-link>
      <router-link to="/red">Red</router-link>
    </nav>
    
    <router-view></router-view>
    
  </div>
</template>

L'exemple au complet

Voici le code complet de App.vue, et le résultat obtenu :

App.vue
<template>
  <div id="app">
    <h1>Router Application</h1>
    
    <nav>
      <router-link to="/blue">Blue</router-link>
      <router-link to="/red">Red</router-link>
    </nav>
    
    <router-view></router-view>
    
  </div>
</template>

<script>
    // importation des composants qui seront intégrés dynamiquement
    import Blue from "@/components/Blue";
    import Red from "@/components/Red";

    // importation des packages pour faire fonctionner VueRouter
    import Vue from 'vue' ;
    import VueRouter from 'vue-router' ;
    
    // DÉCLARATION DES ROUTES (un mapping URL -> composant à charger)
    const routes = [
      { path: '/blue', component: Blue },
      { path: '/red', component: Red },
    ]
    
    // instanciation du router
    const router = new VueRouter({
      routes // short for `routes: routes`
    })
    Vue.use(VueRouter) ;

    export default {
      name: 'App',
      router // déclaration du router dans l'application
    }
</script>

<style>
    #app {
      font-family: Avenir, Helvetica, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      text-align: center;
      color: #2c3e50;
      margin-top: 60px;
    }
    
    nav{
      margin: 20px 0;
      padding: 5px;
      background: lightgray;
    }
    nav *{
      margin: 20px 5px;
    }
</style>
Résultat.
(Vous verrez mieux l'effet des routes dans la barre d'adresse en cliquant ici)

NB: Nous ne le traiterons pas ici, mais dans la documentation officielle, vous pourrez constater qu'il est aussi possible de définir des routes imbriquées (routage dans un sous-composant).

Segments Dynamiques

Grâce aux segments dynamiques, VueRouter permet d'utiliser une partie d'URL pour envoyer des informations aux composants.

Prenons l'exemple d'un nouveau composant Square.vue qui ressemble fortement à Blue.vue et Red.vue, à la différence près que la couleur qu'il affiche n'est pas prédéfinie (il est donc plus générique).

On décide ici que la couleur sera donnée dans l'URL par un segment placé après la route du composant.

  • La route du composant sera nommée /square.
  • Le paramètre donnant la couleur sera le code couleur CSS à afficher (ex. /yellow).

L'URL pour afficher le composant Square.vue avec la couleur yellow sera donc:
http://localhost:8080/#/square/yellow.

Ajout de route paramétrée

On va donc ajouter une route avec un segment dynamique vers le composant Square :

Dans le script de App.vue
...
import Square from "@/components/Square"
...
const routes = [
      ...
      { path: '/square/:color', component: Square},
]

NB: on suppose ici que Square.vue a déjà été créé -> cf. le code ci-après...

Le signe : indique où démarre le segment dynamique qui servira de paramètre dans la route qui mène au composant Square.

Dans notre exemple, ce paramètre est donc nommé color, et il sera remplacé par la chaine qui suit la route /square/.

En d'autres termes, dans http://localhost:8080/#/square/yellow, le paramètre color aura pour valeur yellow.

Ajout de liens paramétrés

Puisque cette route contient un segment dynamique, on peut l'utiliser avec différentes valeurs de paramètre. Nous allons donc ajouter 2 router-link qui mènent vers le composant Square.vue, en la paramétrant avec les couleurs yellow et green.

Dans le template de App.vue
...
<router-link to="/square/yellow">Yellow</router-link>
<router-link to="/square/green">Green</router-link>
...

Paramètres dans le sous-composant : l'objet $route

Bien entendu, pour que tout cela fonctionne, il faut avoir créé le (sous-)composant Square.vue en faisant en sorte qu'il configure sa couleur avec le paramètre color indiqué dans l'URL.

L'utilisation d'un VueRouter crée un objet $route accessible dans toute l'application (y compris dans les sous-composants), et qui permet d'accéder aux parties de l'URL.

Pour ce qui nous intéresse ici, $route possède un attribut params qui contient lui-même les paramètres provenant d'un segment dynamique.

On pourra donc récupérer la valeur du paramètre color avec this.$route.params.color.

Le code du composant Square.vue est donc le suivant :

Square.vue
<template>
<div id="square-comp" class="square" :style="{ background: color}">
  Square : {{ color }}
</div>
</template>

<script>
export default {
  name: "Square",
  data(){return{
    color: this.$route.params.color // récupération du paramètre
  }},
  watch: {
    '$route' (to, from){ // il faut mettre un watcher au cas où le paramètre change...
      console.log(from)
      this.color = to.params.color
    }
  }
}
</script>

<style scoped>
  @import "square.css";
</style>        

Vous aurez remarqué qu'on a aussi ajouté un watch qui permet de "surveiller" l'objet $route et de réagir quand il change.

Ceci vient du fait que si l'application passe de http://localhost:8080/#/square/yellow à http://localhost:8080/#/square/green (par exemple en cliquant sur un lien, puis sur l'autre), le composant visé est 2 fois le même (Square.vue). Par souci d'efficacité, Vue ne le recharge pas, les data ne sont donc pas re-calculées, et part conséquent, la valeur de l'attribut this.color reste la même -> la couleur du composant ne change pas.

NB: ne pas confondre l'attribut this.color avec le paramètre this.$route.params.color !

Le watch sur $route permet de savoir que, même si le composant visé n'a pas changé (et n'a donc pas été rechargé), l'URL a quand à elle subi une modification (ici, une nouvelle valeur pour le paramètre this.$route.params.color, qui doit être répercutée sur l'attribut this.color).

L'exemple au complet

App.vue
<template>
  <div id="app">
    <h1>Router Application</h1>

    <nav>
      <router-link to="/blue">Blue</router-link>
      <router-link to="/red">Red</router-link>
      <router-link to="/square/yellow">Yellow</router-link>
      <router-link to="/square/green">Green</router-link>
    </nav>

    <router-view></router-view>

  </div>
</template>

<script>
import Vue from 'vue' ;
import VueRouter from 'vue-router' ;

import Blue from "@/components/Blue";
import Red from "@/components/Red";
import Square from "@/components/Square";

const routes = [
  { path: '/blue', component: Blue },
  { path: '/red', component: Red },
  { path: '/square/:color', component: Square},
]

const router = new VueRouter({
  routes // short for `routes: routes`
})

Vue.use(VueRouter) ;

export default {
  name: 'App',
  router,
}
</script>

<style>
...
</style>
Résultat.
(Vous verrez mieux l'effet des routes dans la barre d'adresse en cliquant ici)

Les paramètres en tant que props

Il est aussi possible de modifier légèrement le code de l'application et du sous-composant pour faire en sorte que, au lieu d'utiliser this.$route.params, les paramètres soient envoyés en tant que propriétés (props).

Pour ce faire, il suffit de changer la route déclarée dans App.vue comme suit :

Dans le script de App.vue
...
    { path: '/square/:color', component: Square, props: true},
...

Et la nouvelle version de Square.vue, qui n'utilise plus $route, et n'a plus besoin de watch puisque, comme vous le savez ;), les props sont automatiquement mises à jour dans les composants qui les utilisent.

Square.vue avec props
<template>
<div id="square-comp" class="square" :style="{ background: color}">
  Props : {{ color }}
</div>
</template>

<script>
export default {
  name: "Square",
  props:{
    color: String
  }
}
</script>

<style scoped>
  @import "square.css";
</style>

Il est possible de faire plus de choses avec les props (et les segments dynamiques en général). Pour plus d'infos c'est ici.

L'objet $router

La déclaration d'un VueRouter dans une application a pour effet de mettre à disposition un nouvel objet Javascript nommé $router.

$router fournit des méthodes qui permettent de naviguer (changer d'URL) en Javascript.

Les méthodes de navigation de $router sont :

  • push(location, onComplete?, onAbort?) : change l'URL en cours de visualisation et ajoute une entrée dans l'historique de navigation.
    C'est cette méthode qui est appelée quand on clique sur un router-link.
    onComplete? et onAbort? permettent (optionnellement) de créer des handlers.
  • replace(location, onComplete?, onAbort?) : comme push mais sans ajouter une entrée dans l'historique de navigation.
  • go(n) : permet de "voyager" dans l'historique de navigation.
    (ex. pour remonter d'un cran : this.$router.go(-1)).

Exemple avec un bouton

Pour tester, on peut étendre la barre de navigation de App.vue en ajoutant un bouton dont le click appellera une méthode goto qui utilisera $router pour changer l'URL.

Dans App.vue
<template>
  <div id="app">
    <h1>Router Application</h1>

    <nav>
      ...
       <button @click="goto('white')">White</button>
    </nav>

    <router-view></router-view>

  </div>
</template>

<script>

...

export default {
  name: 'App',
  router,
  methods:{
    goto(c){
      this.$router.push("/square/" + c) ;
    }
  }
}
</script>
...
Résultat.
(Vous verrez mieux l'effet des routes dans la barre d'adresse en cliquant ici)

Il est possible de faire bien plus de choses avec VueRouter (routes imbriquées, passer des objets complexes, faire des redirections, des alias, etc.). Pour aller plus loin je vous invite à étudier la documentation officielle .