Développement d'applications mobiles

Partie 2 - Vue.js et Progressive Webapps

Cours 2 - Faire du style stylé



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é du premier cours

On est maintenant capable de

  • Créer une application vue avec Vue CLI
  • Définir des props/datas dans un composant
  • Savoir que la réactivité de VueJS est bi-directionnelle
  • D'afficher une expression {{ maProp }}
  • De binder un attribut v-bind:href="maProp" ou :href="maProp"
  • D'effectuer du rendu conditionnel avec v-if,v-elseif et v-else
  • De rendre des collections v-for="e in array" v-bind:key="e.id">
  • D'intercepter des events v-on:click="method" ou @click="method"

Ce qui nous manque

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

😎 Du style (parce que là c'est affreux)

⌛ Un peu de pratique (créer nos propres composants)

C'est l'objectif de notre séance

5. Faire du style stylé

Dans Vue, la balise style accepte plusieurs langages: css, sass, less, scss...

Il est préférable d'utiliser un langage de haut-niveau (e.g. less ou scss) plutôt que css directement.
Sass, less, scss apportent des notions utiles (héritage, mixins etc...) tout en compilant vers du css derrière.

Dans ce cours, on utilisera du css pour rester accessible à tous, mais vous êtes invités à utiliser scss ou less par exemple sur de "vrais" projets.

Plutôt que de devoir faire tout notre style à la main et gérer le responsive nous-même, j'aimerais utiliser un framework css.

Connaissez-vous des frameworks CSS fournissant des briques responsives par défaut ?

→ Bulma, tailwindcss, Bootstrap...

tailwindcss est très populaire, je suis personnellement un fan de Bulma & Buefy (libraire Vue au-dessus de Bulma)

Bulma en 1 minute

Bulma est un framework css qui, une fois ajouté à votre projet Vue, va rendre disponible plein de classes css utilitaires.

Vous pourrez ainsi facilement gérer les polices, les tailles et la disposition responsive, le dark-mode, les animations...

Voir exemple d'utilisation de Bulma

Installer Bulma (version propre) - étape 1

npm install bulma

(tout le monde sait ce que fait npm install ?)

  • Cherche la dernière version de "bulma" dans les repos npm
  • Ajoute au fichier package.json une entrée pour bulma (le --save est inutile depuis npm 5.0)
  • Télécharge le code de bulma dans le dossier node_modules

Un autre dévelopeur qui clone le repo n'aura qu'à faire npm install pour avoir toutes les dépendances listées dans le package.json

Installer Bulma (version propre) - étape 1

npm install bulma

Installer Bulma (version propre) - étape 2

Si on avait le temps de détailler : installer le scss via webpack

Installer Bulma (version crado)

Ici on se contentera d'ajouter bulma.css à notre index.html

<link 
	rel="stylesheet"
	href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css" />

Bulma est installé !

On peut maintenant utiliser les classes bulma.

Exercice 7: premiers pas avec Bulma

  • Installer bulma en suivant la procédure détaillée
  • Fixer la taille à 400px dans App.vue :
  • Afficher le composant Composter sous forme de card Bulma
  • Obtenir le rendu attendu à gauche (il vous faudra utiliser l'exemple de .card et également la classe .button)

Responsive design

Une des contraintes du développement Web/mobile est d'avoir à gérer des résolutions multiples avec des ratios hétérogènes (mobile portait/paysage, tablette, laptop, desktop, widescreen...)

Il existe de très nombreuses approches pour gérer le responsive. Voyons un exemple simple basé sur Bulma.

Les columns Bulma

Un bon moyen de faire du reponsive simple est de structurer son site en colonnes.
Bulma fournit des classes pour facilement gérer un tel découpage: les columns.

Les columns Bulma

Un container de colonnes doit avoir la classe columns

Il contient autant d'élement column que souhaité

Par défaut, bulma fera en sorte que les colonnes aient une taille égale, sauf sur mobile où chaque colonne prend toute la largeur.

Vous avez la main sur la taille des colonnes, penser à ajouter is-multiline si vous souhaitez autoriser le container columns à prendre plus d'une ligne quand la somme des tailles dépasse 100%

Moitié de l‘écran

Un quart de l‘écran

Un autre quart de l‘écran

Un tiers, nouvelle ligne créée

commit : Bulma columns - size example

Vous pouvez définir des tailles différentes selon la résolution (c'est tout l'intérêt).

5 breakpoints définis par défaut:

  • mobile (jusqu'à 768px)
  • tablet (jusqu'à 1023px)
  • desktop (jusqu'à 1215px)
  • widescreen (jusqu'à 1407px)
  • fullhd

5 breakpoints définis par défaut:

  • mobile (jusqu'à 768px)
  • tablet (jusqu'à 1023px)
  • desktop (jusqu'à 1215px)
  • widescreen (jusqu'à 1407px)
  • fullhd

 

Quart de l‘écran desktop, Moitié tablet

Quart de l‘écran desktop, Moitié tablet

Tiers de l‘écran desktop, Tiers tablet

Un quart dans tous les cas sauf sur widescreen

commit : Bulma columns - size example (responsive)

Exercice 8: rendre compostmap responsive

  • Modifier App.vue pour qu'il contienne 150 composants Composter
  • Faire en sorte qu'il y ait :
    - 1 composant par page sur mobile
    - 2 par page sur tablette
    - 3 par page sur desktop
    - 4 page sur grand écran

Pour éviter l'aspect "massif" sur grande résolution, sur beaucoup de sites, on voit une taille au-delà de laquelle le contenu ne s'étire plus, on fixe une taille maximale et on se contente de centrer.

Vous pouvez ajouter la classe "container" pour fixer la taille au-delà d'une certaine résolution (voir toutes les options possible ici)

commit : Bulma add container
  • Bulma fournit énormément de classes utilitaires (nous n'avons vu qu'une infime portion)
  • Faites des tutos, lisez la doc, regardez Bootstrap ou tailwindcss si vous préférez (c'est le même esprit)

Exercice 9: à vous de coder

Réaliser les développments nécessaires à l'obtention de cette vidéo :

Imaginons que je souhaites changer la façon d'afficher le titre dans le template

Composter {{ composterId }}

En affichant un message différent selon que l'id soit pair ou non

Composter {{ composterId }} {{ (composterId %2 == 0) ? 'pair' : 'impair'}}
{{ 'Composter ' + composterId + ((composterId %2 == 0) ? 'pair' : 'impair') }}

{{ 'Composter ' + composterId + ((composterId %2 == 0) ? 'pair' : 'impair') }}

Imaginons que je souhaite afficher ce nom à plusieurs endroits et que je le copie-colle plusieurs fois dans mon template.

Quels problèmes ce copier-coller pose-t-il ?

  • Lisibilité (dur de comprendre que c'est le nom du composteur)
  • Maintenabilité (si on change le calcul du nom il faudra changer, risque d'oublier une occurence)
  • Performance(le calcul du nom est effectué partout où il est affiché plutôt qu'une seule fois)
  • Lisibilité (dur de comprendre que c'est le nom du composteur)
  • Maintenabilité (si on change le calcul du nom il faudra changer, risque d'oublier une occurence)
  • Performance(le calcul du nom est effectué partout où il est affiché plutôt qu'une seule fois)

Il nous faudrait un mécanisme qui permet :

  • d'effectuer un calcul prenant en paramètre plusieurs props/data
  • de mettre en cache le résultat dans une propriété (componentTitle)
  • d'utiliser cette propriété le template ({{ componentTitle }})
  • de recalculer uniquement si un des props/datas utilisé change

Il nous faudrait un mécanisme qui permet :

  • d'effectuer un calcul prenant en paramètre plusieurs props/data
  • de mettre en cache le résultat dans une propriété (componentTitle)
  • d'utiliser cette propriété le template ({{ componentTitle }})
  • de recalculer uniquement si un des props/datas utilisé change

C'est la définition d'une computed property VueJS

C'est la définition d'une computed property VueJS

export default {
	name: "Composter",
	props: {
	  composterId: String
	},
	computed: {
	  componentTitle() {
	   let evenOrOdd = 'impair'
	   if (this.composterId %2 == 0) {
	     evenOrOdd = 'pair'
	   }
	   return 'Composter ' + this.composterId + ' (' + evenOrOdd + ')'
	  }
	}
}
export default {
	name: "Composter",
	props: {
	  composterId: String
	},
	computed: {
	  componentTitle() {
	   let evenOrOdd = 'impair'
	   if (this.composterId %2 == 0) {
	     evenOrOdd = 'pair'
	   }
	   return 'Composter ' + this.composterId + ' (' + evenOrOdd + ')'
	  }
	}
}

componentTitle est une computed Property, utilisable n'importe où dans le template et rafraîchie uniquement si un des props/datas utilisés dans le calcul changent.

commit : Computed property example

Exercice 10: computed properties

  • Faire en sorte que isOpen ne soit plus une data mais une computed property (vraie si id est impair, faux sinon)
  • Modifier le calcul de isOpen pour qu'on ne puisse pas être ouvert si il n'y a aucun horaire d'ouverture de défini
  • Ajoutez/supprimez des jours d'ouvertures. L'ouverture est-elle correctement calculée ?

La différence entre un composteur ouvert ou fermé n'est pas flagrante...

On aimerait que le css de la carte change de couleur en fonction de isOpen

<div class="card"
style="background-color: #A6D785;border: 2px solid #3B5E2B;">
<div class="card"
style="background-color: #F5F5F5;border: 2px dashed #C0C0C0;">
<div class="card"
style="background-color: #A6D785;border: 2px solid #3B5E2B;">
<div class="card"
style="background-color: #F5F5F5;border: 2px dashed #C0C0C0;">

Comment faire changer le style avec les mécanismes déjà vus ?

  • v-if n'est pas adapté car on veut changer la valeur de l'attribut style pas masquer la div
  • v-bind semble parfait, style est un attribut ! Essayons
  • v-bind semble parfait, style est un attribut ! Essayons
:style="{
	backgroundColor: isOpen ? '#A6D785' : '#F5F5F5',
	borderColor: isOpen ? '#3B5E2B' : '#C0C0C0',
	borderStyle: isOpen ? 'solid' : 'dashed',
	borderWidth: '2px'
}">

À noter que l'on peut utiliser n'importe quelle data/prop :

borderWidth: composterId + 'px'
commit : Style binding example

Utiliser :style fonctionne parfaitement, et sera approprié dans certains usages

Mais dans notre exemple, ce serait bien plus propre de définir deux classes CSS .composter-opened et .composter-closed

Mais dans notre exemple, ce serait bien plus propre de définir deux classes CSS .composter-opened et .composter-closed

Ajoutons une balise <style> après <template> et <script>

Utilisons une nouvelle fois v-bind pour binder une ou plusieurs classes en fonction de conditions

<div class="card" :class="{
	'composter-opened': isOpen,
	'composter-closed': !isOpen
}">
commit : Class binding example

Exercice 11: binding de style et de classe

  • Binder l'attribut style des .card-image pour que le curseur soit pointer si il y a plus de 5 horaires d'ouvertures
  • Créer une div contenant l'adresse et le texte d'ouverture. Faire en sorte que le texte soit gras si le lieu est ouvert, et que la couleur de fond change en fonction de la ville

Point d'avancement

On est maintenant capable

  • D'installer et utiliser les classes Bulma (.cards...)
  • De mettre en place un design responsive grace aux columns
  • Définir des computed properties pour factoriser des calculs
  • D'utiliser v-bind sur les classes et le style pour effectuer du styling dynamique en fonction des props/datas

Ce qui nous manque

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

☰ Un menu et une structure en différentes pages

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

C'est l'objectif de notre séance


D'ici le prochain cours

- Entraînez-vous

- Harcelez-moi (par mail et/ou sur discoord)



Alex morel
Code Lutin