Vue JS 3 - Utilisation de propriétés calculées dans le css d'un composant
par
, 01/09/2021 à 13h15 (1629 Affichages)
Bonjour
Un besoin que je rencontre de temps en temps lorsque je code mes composants VueJS, c'est celui de générer des styles css dynamiquement. L'une des possibilités réside dans l'utilisation de variables css. Mais comment s'y prendre ?
Tout d'abord, dans ce billet j'utilise VueJS 3 ainsi que la nouvelle API Composition de VueJS. C'est-à-dire qu'au lieu d'utiliser les champs data, mounted, computed..., j'utilise le champ setup ainsi que les nouvelles fonctions mises en place (onMounted, ref, computed, watch, ...).
Afin d'illustrer le concept, je pars du principe que l'on souhaite créer un simple composant carré (MyBox) de couleur rouge, dont la taille est configurable via un attribut personnalisé sizePx.
Admettons pour l'instant que le composant dispose d'une taille fixe de 60px :
Rien de bien compliqué en soi.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 <template> <div id="root"></div> </template> <script> export default { name: 'MyBox', props: { } } </script> <style scoped> #root { width: 60px; height: 60px; background-color: red; } </style>
Ajoutons la propriété sizePx (lignes 8-12), qui n'aura pas encore d'effet sur la taille de notre composant :
On pourra, moyennant importation et déclaration du composant, l'utiliser de la manière suivante : <MyBox :sizePx="100" />A l'heure où ce billet a été rédigé, il existait une proposition de syntaxe afin de pouvoir injecter des variables de notre script du composant dans le css du même composant :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 <template> <div id="root"></div> </template> <script> export default { name: 'MyBox', props: { sizePx: { type: Number, required: true, } } } </script> <style scoped> #root { width: 60px; height: 60px; background-color: red; } </style>
- la variable en question doit être une référence
- dans le css en question, la variable est référencée à l'aide de la fonction v-bind
Il est fort probable que votre éditeur en ligne favori (par exemple, j'ai testé avec StackBlitz), ne supporte pas cette syntaxe : mieux vaut créer un projet avec Vue CLI (c'est-à-dire avec le générateur de projet Vue JS depuis le terminal).
Voici ce que cela peut donner avec notre composant :
Cette fois-ci, notre carré prend la dimension que l'on lui a indiqué.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 <template> <div id="root"></div> </template> <script> import {computed} from 'vue'; export default { name: 'MyBox', props: { sizePx: { type: Number, required: true, } }, setup(props) { const size = computed(() => props.sizePx + 'px'); return {size}; }, } </script> <style scoped> #root { background-color: red; width: v-bind(size); height: v-bind(size); } </style>
Pour cela :
- j'ai crée une variable size, liée à la propriété sizePx (ligne 16)
- je l'ai référencée dans le css à l'aide de la fonction v-bind (lignes 26-27)
Ici, j'ai donné à la variable size la valeur de sizePx avec l'unité Mais je peux aussi ne donner que la valeur et ajouter l'unité lors de l'appel à la fonction v-bind dans le css :
Vous pouvez remarquer:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 <template> <div id="root"></div> </template> <script> import {computed} from 'vue'; export default { name: 'MyBox', props: { sizePx: { type: Number, required: true, } }, setup(props) { const size = computed(() => props.sizePx); return {size}; }, } </script> <style scoped> #root { background-color: red; width: calc(v-bind(size) * 1px); height: calc(v-bind(size) * 1px); } </style>
- l'appel à la fonction calc
- la multiplication de la valeur par l'unité 1px
Rien nous empêche de changer d'unité : je peux aussi écrire width: calc(v-bind(size) * 0.2vw);,width: calc(v-bind(size) * 3px); ...
Enfin, j'ai eu besoin dans un de mes projets d'une utilisation plus complexe :
Ainsi le calcul de la valeur controlsWidth (ligne 12) est très dynamique : notamment avec l'intégration de la fonction calc dans la chaîne résultante en orientation paysage.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 <script> import { ref, onMounted, onBeforeUnmount } from "vue"; export default { setup() { const controlsWidth = ref(0); const cellsSize = ref(0); function adjustLayoutDirection() { ... controlsWidth.value = isPortrait ? cellsSize.value * 8 + "px" : "calc(100% - " + cellsSize.value * 8 + "px)"; } onMounted(() => adjustLayoutDirection); window.addEventListener("orientationchange", adjustLayoutDirection); onBeforeUnmount(function () { window.removeEventListener("orientationchange", adjustLayoutDirection); }); return { controlsWidth, cellsSize }; } } </script> <style scoped> #controls { display: flex; flex-direction: column; justify-content: space-evenly; align-items: center; width: v-bind("controlsWidth"); } </style>
Voilà, j'espère avoir bien présenté le concept. N'hésitez pas à aller voir la page de la proposition de syntaxe, et à expérimenter cette fonctionnalité.