Développement d'applications mobiles

Partie 2 - Vue.js et Progressive Webapps

Cours 4 - Rest, Promises & ouverture



Alex morel
Code Lutin

Plan du cours

Cours 1: introduction à Vue.js

Cours 2: Style (Bulma & class-binding)

Cours 3: Client-side routing

Cours 4: Rest, promises, ouverture

Résumé des trois premiers cours

On est maintenant capable de

  • Créer une application vue avec Vue CLI
  • Définir des composants avec leurs props/data
  • Utiliser v-bind, v-if, v-on, v-for
  • Mettre en place un framework css (Bulma) et l'utiliser
  • Définir des computed properties pour factoriser des calculs
  • Faire du binding de style et de classes CSS
  • Comprendre ce qu'est le Client-side routing
  • Mettre en place et configurer un router vue sur des cas d'usages simples

Ce qui nous manque

Qu'est ce qu'il manque à notre application pour être utile ?

⌚ Gérer l'asynchronicité (Promise)

🌐 Aller chercher des vraies données depuis un serveur REST

✏ Modifier les données au sein de l'application

C'est l'objectif de notre séance

Avant de pouvoir effectuer des requêtes réseaux, il va d'abord nous falloir étudier la gestion de l'asynchronicité en JS.

7. Les promesses

JS fournit un mécanisme pour gérer l'asynchronicité: les promesses

let promise = new Promise(function(resolve, reject) {
  // EXECUTER DU CODE QUI PREND DU TEMPS
});

Une promesse = bout de code asynchrone

Une promesse peut être rejetée en cas d'échec...

let promise = new Promise(function(resolve, reject) {
  // EXECUTER DU CODE QUI PREND DU TEMPS
  // Problème! Je rejette la promesse
  reject("There is a problem")
});

... et résolue (avec un résultat) en cas de succès

let promise = new Promise(function(resolve, reject) {
  // EXECUTER DU CODE QUI PREND DU TEMPS
  // Succès ! Je resolve avec le result
  var result = { first_name: 'Paul', last_name: 'Mc Cartney'}
  resolve(result);
});

On parle de fonction/méthode asynchrone pour désigner une méthode retournant une promesse

function asyncFunction() {
  return new Promise(function(resolve, reject) {
    // EXECUTER DU CODE QUI PREND DU TEMPS
    // Succès ! Je resolve avec le result
    var result = { first_name: 'Paul', last_name: 'Mc Cartney'}
    resolve(result);
  });
}

L'appel d'une fonction asynchrone ne bloque pas le fil d'exécution contrairement à une méthode synchrone

L'appel d'une fonction asynchrone ne bloque pas le fil d'exécution contrairement à une méthode synchrone

console.log("coucou");
var result = asyncFunction();
console.log("Tout de suite appelé");
// Attention ici asyncFunction() n'a peut pas terminé son exécution

Quel est le type de result ?

Quel est le type de result ?

C'est une promesse, qui pour l'instant n'est pas résolue ni rejetée (on dit qu'elle est pending)

Comment exécuter du code lorsque la promesse est résolue/rejetée ?

Avec la méthode then

asyncFunction().then(fonctionEnCasDeResolution, fonctionEnCasDechec)

(et éventuellement catch)

asyncFunction()
	.then(fonctionEnCasDeResolution)
	.catch(fonctionEnCasDechec)

Comment exécuter du code lorsque la promesse est résolue/rejetée ?

asyncFunction()
	.then(fonctionEnCasDeResolution)
	.catch(fonctionEnCasDechec)
asyncFunction()
  .then(result => {
	// Ici la promesse est résolue, on a accès au résultat
	console.log("Success ! ", result)
  })
  .catch(failure => {
	// Ici la promesse est rejetée, on a accès à la cause d'échec
    console.err("Failure :( ", failure)
  })
)

Exercice 17: une promesse est une promesse

  • Placez-vous dans le fichier main.js
  • Créer une fonction asynchrone asyncFailIfMust(mustFail) qui est rejetée si mustFail vaut vrai et retourne "success" dans le cas contraire
  • Appeler la méthode en faisant varier le paramètre mustFail. Pour les deux appels, afficher un succès avec console.log() et un échec avec console.error()
  • Utiliser setTimeout() pour faire en sorte que asyncFailIfMust mette 3 secondes à s'exécuter, et ajouter des logs avant/après l'appel, pour obtenir le résultat suivant:

On peut effectuer de l'orchestration de promesses : comme les chaîner les unes à la suite des autres...

...lancer plusieurs tâches asynchrones et être notifié quand elles sont toutes résolues

Ce n'est pas l'objet du cours, mais vous pouvez regarder la doc de Promise.all par exemple

Je ne suis personnellement pas fan de la syntaxe pour des opérations simples (inutilement verbeux).

On peut aussi attendre la résolution de la promess grâce au mot-clé await

try {
    var result = await asyncFunction();
    console.log("Grâce à await on a attend la fin de l'execution");
} catch (reject) {
    console.error("Promesse rejetée")
}
try {
    var result = await asyncFunction();
    console.log("Grâce à await on a attend la fin de l'execution");
} catch (reject) {
    console.error("Promesse rejetée")
}

Les développeurs se sont dit qu'en utilisant await à tord et à travers, on risquait de bloquer le navigateur

Il n'est donc possible d'utiliser await que dans une fonction explicitement définie comme async

Il n'est donc possible d'utiliser await que dans une fonction explicitement définie comme async

async someFunction() {
  try {
    var result = await asyncFunction();
    console.log("Grâce à await on a attend la fin de l'execution");
  } catch (reject) {
    console.error("Promesse rejetée")
  }
}

Exercice 18: wait wait l'ami

  • Placez-vous dans le fichier main.js
  • Modifer le code précédent pour await tous les appels à asyncFailIfMust()
  • Vous devez obtenir les logs suivants:

Point d'avancement

On est maintenant capable

  • D'écrire une méthode asynchrone (qui renvoie une Promise)
  • De rejeter ou résoudre une Promise
  • De réagir à la résolution d'une promise pending avec .then() et .catch()
  • D'utiliser async/await pour simplifier la syntaxe
  • On sait qu'il est possible de chainer/composer des promises
  • il ne nous faut rien de plus pour pouvoir effectuer des requêtes à un serveur

8. REST in peace

On veut faire en sorte que notre application Vue soit capable d'effectuer des requêtes REST (pour interragir avec un serveur).

Tous les navigateurs récents sont munis d'une API native, fetch, qui permet d'effectuer des requêtes HTTP vers un serveur REST.

fetch('http://someserver.com/api/someroute')
  .then(
    function(response) {
      if (response.status !== 200) {
        console.log('Looks like there was a problem. Status Code: ' +
          response.status);
        return;
      }

      // Examine the text in the response
      response.json().then(function(data) {
        console.log(data);
      });
    }
  )
  .catch(function(err) {
    console.log('Fetch Error :-S', err);
  });

Doc complète disponible ici

Il existe également une libraire standalone js, Axios, qui fournit des fonctionnalités similiares à fetch

 axios.get('http://someserver.com/api/someroute')
	.then(response => {
	  console.log(response.data)
	})
	.catch(error => {
		console.log(error)
	})
}

Doc complète disponible ici

Axios vs Fetch

Chaque libraire dispose de ses avantages, les deux solutions se valent globalement (voir comparatif ici)

fetch est natif (dispo via le navigateur rien à installer), Axios gère plus facilement la compatiblité avec les vieux navigateurs (faisable avec des polyfills sur fetch) et fournit du sucre syntaxique

On partira sur Axios pour notre projet, mais fetch reste parfaitement valable.

Axios

On ne va voir qu'une infime portion des fonctionnalités d'Axios, qui permet notamment

  • D'effectuer des requêtes REST (GET, POST, PUT, DELETE)
  • Ajouter des headers à chaque requête (token JWT...)
  • Sérialiser/Déserialiser les résultats string/json
  • Définir des timeouts si les requêtes sont trop lentes
  • Intercepter toutes les requêtes pour ajouter du comportement (logout, logging...)
  • Annuler des requêtes en cours
  • ...

 axios.get('http://someserver.com/api/someroute')
	.then(response => {
	  console.log(response.data)
	})
	.catch(error => {
		console.log(error)
	})
}

Bon, reste à savoir quand appeler ce code...

Au chargement du composant ComposterList ?

Au chargement du composant ComposterList ?

Tous les composants Vue ont un cycle de vie (créé, monté, affiché, détruit...)

À chaque changement d'état, une méthode est appelée (lifecycle hooks), dans laquelle on peut brancher du comportement

Il existe de nombreux hooks (voir liste complète ici), par exemple:

  • beforeCreate(): au moment où vue commence à créer le composant
  • mounted(): le composant est prêt à être affiché
  • updated(): à chaque fois que le DOM est rafraîchi suite à un changement de props/data
  • mounted(): le composant est prêt à être affiché

import axios from "axios";

export default {
  name: "MyComponent",
  data() {
    return {
      myList: new Array()
    };
  },
  mounted() {
    axios
      .get("http://someserver.com/api/someroute")
      .then((response) => {
        this.myList = response.data
      })
      .catch((error) => {
        console.log(error);
      });
  },
};

Exercice 19: des vraies données !

  • Dans ComposterList, utilisez axios pour récupérer la liste des composteurs (via cette url)
  • Modifiez Composter pour afficher les informations à votre disposition:
  • Afficher différement les composteurs de quartier des composteurs de résidence, et ajoutez un lien vers le site + google maps (https://maps.google.com/?q=LATITUDE,LONGITUDE)

Axios fonctionne exactement pareil pour les requêtes POST (envoi d'image, de données..)

Dans un "vrai" projet, tout le code purement logique serait extrait dans un service (SOC)

Point d'avancement

On est maintenant capable de

  • Créer une application vue avec Vue CLI
  • Définir des composants avec leurs props/data
  • Utiliser v-bind, v-if, v-on, v-for
  • Mettre en place un framework css (Bulma) et l'utiliser
  • Faire du binding de style et de classes CSS
  • Faire du Client-side routing avec le vue router
  • Éxecuter du code asynchrone avec les promesses et async/await
  • Utiliser axios pour interragir avec des API rests

Ce qui nous manque

Qu'est ce qu'il manque à notre application pour être utile ?

🧐 Quelques notions Vue (events...)

✅ Comment Debugger/Tester notre app

🦋 Comment déployer notre app (sur nos builds, sur serveur, sur mobile)


Merci pour votre attention !

- Vos retours (trop dur/facile rapide/lent)

- Ma porte reste ouverte (par mail et/ou sur discoord et/ou sur twitter)



Alex morel
Code Lutin