Cet article traite de la création d'un composant Select avec RiotJS en utilisant BeerCSS. Avant de commencer, assurez-vous d'avoir une application de base Riot, ou lisez mes articles précédents.
n'hésitez pas à consulter la documentation de RiotJS si nécessaire: https://riot.js.org/documentation/
Un Select fournit un menu d'options : L'objectif est de créer un composant Select avec un design BeerCSS et d'écouter les événements de changement.
Base du composant Select
Tout d'abord, créez un nouveau fichier nommé c-select.riot dans le dossier des composants. Le préfixe c-
signifie "composant", une convention de nommage utile et une bonne pratique.
Écrivez le code HTML suivant (CSS trouvé dans la documentation BeerCSS) dans ./components/c-select.riot:
class="field suffix
{props?.label ? ' label' : null }
{props?.error ? ' invalid' : null }
">
if={ !props?.loading } each={option in props.options} value={ option } selected={ option === props.value ? true : null }>{ option }
if={ props?.label }>{ props.label }
if={ !props?.loading && !props?.img && !props?.icon} >arrow_drop_down
if={ !props?.loading && !props?.img && props?.icon} >{ props.icon }
if={ !props?.loading && props?.img } class="circle" src={ props.img }>
if={ props?.loading } class="circle">
class="helper" if={ props?.helper && !props?.error }>{ props.helper }
class="error" if={ props?.error }>{ props.error }
Décomposons le code :
- Les balises
etdéfinissent une balise racine personnalisée, portant le même nom que le fichier. Vous devez l'écrire ; sinon, cela pourrait créer des résultats inattendus. Utiliser la balise
comme balise racine ou redéfinir des balises HTML natives est une mauvaise pratique, donc commencer par c- est un bon nommage.
- La liste des options passées en tant qu'attributs est un tableau de chaînes de caractères, par exemple
["red", "green", "blue"]
, et elle est parcourue grâce à l'attribut Riot each comme suit :each={option in props.options}
. Une nouvelle baliseest créée pour chaque élément de la liste.
- Pour chaque élément de la liste, deux attributs importants sont définis sur la balise
:
- L'attribut
value
obtient la valeur de l'élément de la liste, soit red, green ou blue. L'événementchange
émettra la valeur si l'option est sélectionnée. - L'attribut
selected
définit siprops.value
est égal à la valeur de l'option.
- L'attribut
- Le composant a un état de chargement : si
props?.loading
existe, les balises d'option, l'icône et l'image sont masquées avecif={ !props?.loading }
; enfin, il affichera une icône de chargement. - Le select a une icône par défaut arrow_drop_down, et il est possible de la remplacer par une autre icône Google Material en passant l'attribut icon. L'icône n'est affichée que s'il n'y a pas d'image et pas d'état de chargement.
- Au lieu d'une icône pour l'élément select, une image avec l'attribut
img
peut être incluse. L'image n'est pas imprimée si la propriété de chargement est activée. - Pour aider l'utilisateur, une propriété helper peut être ajoutée ; elle affichera un message sous l'élément select. Si props?.helper existe, la classe helper est ajoutée au composant.
- Si une erreur se produit, il est possible d'afficher un message d'erreur grâce à l'attribut
error
. Siprops.error
existe, la classeerror
est ajoutée au composant.
Enfin, chargez et instanciez le c-select.riot dans une page nommée index.riot:
style="width:600px;padding:20px;">
style="margin-bottom:20px">Riot + BeerCSS
label="Color" options={ state.options } value={ state.value } onchange={ changed } />
img="./favicon.png" helper={ "Color selected: " + state.value } label="Color" options={ state.options } value={ state.value } onchange={ changed }/>
import cSelect from "./components/c-select.riot"
export default {
components: {
cSelect
},
state: {
options: [
'red',
'green',
'blue',
'purple'
],
value: 'green'
},
changed (ev) {
this.update({ value: ev.target.value })
}
}
Détails du code :
- Les composants sont importés avec
import cSelect from "./components/c-select.riot";
puis chargés dans l'objet Riotcomponents:{}
. - Pour les tests, le composant
cSelect
est instancié deux fois avec
dans le HTML. Le second select prend une image avec l'attributimg
. - Le composant select prend la liste des options dans l'attribut option avec
options={ state.options }
. - La valeur sélectionnée est stockée dans l'objet Riot State
state: { value: 'green' }
. La valeur par défaut sélectionnée est verte. - Si une option est sélectionnée, l'événement
change
est déclenché, et la fonction changed est exécutée pour mettre à jourstate.value
. - Pour mettre à jour l'état d'un composant, la fonction Riot
this.update()
doit être utilisée. Dans notre cas,state.value
obtient la valeur de l'événement, comme suit :this.update({ value: ev.target.value })
.
Voici le HTML généré :
Composant Select Avancé
Un front-end de production obtient souvent une liste d'objets provenant d'une base de données/API, comprenant :
- Un ID sous forme de nombre ou d'UUID sous forme de chaîne de caractères pour la valeur de l'option.
- Une chaîne de caractères différente pour le label de l'option.
Par exemple, fournissons une liste de villes au composant select:
export default {
state: {
list: [
{ id: 0, city: "Paris" },
{ id: 1, city: "London"},
{ id: 2, city: "Berlin"},
{ id: 3, city: "New York"}
]
}
}
Le composant Select actuel ne prend qu'une liste de chaînes de caractères, et des modifications doivent être apportées pour supporter une liste d'objets. Ajoutons le code HTML suivant au composant c-select.riot :
each={option in props.options} value={ option[props.itemValue] } key={ option[props.itemValue] } selected={ option[props.itemValue] === props.value ? true : null } if={ props.itemValue && !props?.loading }>{ option[props?.itemLabel || props?.itemValue] }
Détails du code :
- Pour parcourir tous les objets, l'attribut Riot each est utilisé
each={option in props.options}
. - Pour attribuer une valeur basée sur un objet, la propriété
item-value (props.itemValue)
définit la clé attribuée à l'attribut value. * Pour décomposer l'expressionvalue={ option[props.itemValue] }
:- L'option est un élément de la liste
- Passer la clé props.itemValue entre crochets renvoie la valeur de l'objet.
- Pour attribuer un label basée sur un objet, la propriété
item-label (props?.itemLabel)
définit la clé attribuée à l'étiquette de l'option. SiitemLabel
n'existe pas, il prend itemValue comme étiquette par défaut. - Remarquez
key={ option[props.itemValue] }
: Ajouter l'attributkey
permet à RiotJS d'optimiser la boucle. Si les listes sont immuables, cela améliorera grandement les performances de la boucle. - Enfin, l'option n'est imprimée que si la propriété itemValue existe et que le composant n'est pas en cours de chargement.
- Bonus : une icône Google Material nommée "explore" est passée.
Modifions index.riot et fournissons une liste d'objets au composant Select :
style="width:600px;padding:20px;">
style="margin-bottom:20px">Riot + BeerCSS
icon="explore" helper={ "City selected: " + state.city } label="Cities" options={ state.list } value={ state.city } item-value="id" item-label="city" placeholder="Select a city" onchange={ changed }/>
import cSelect from "./components/c-select.riot"
export default {
components: {
cSelect
},
state: {
list: [
{ id: 0, city: "Paris" },
{ id: 1, city: "London"},
{ id: 2, city: "Berlin"},
{ id: 3, city: "New York"}
],
city: 3
},
changed (ev) {
this.update({ city: parseInt(ev.target.value)})
}
}
Décomposition du code :
- La liste des villes est passée au composant Select avec
options={ state.list }
. - L'attribut
item-value="id"
définit la clé de l'objet utilisée pour la valeur de l'option. - L'attribut
item-label="city"
définit la clé de l'objet utilisée pour le label de l'option. - La valeur
state.city
est mise à jour lorsqu'un événement "change" est déclenché : cela exécutera la fonctionchanged
. Dans notre cas,parseInt
est nécessaire car la valeur de l'événement est une chaîne de caractères, et non un nombre.
Le HTML généré :
Test du composant Select
Il existe deux méthodes pour tester le composant Select, et elles sont couvertes dans deux articles différents :
- Test avec Vitest et Riot-SSR dans un environnement Node
- Test avec Vitest dans un environnement JsDom
Conclusion
Voilà 🎉 Nous avons créé un composant Select avec RiotJS en utilisant BeerCSS. Le code source du select est disponible sur Github : https://github.com/steevepay/riot-beercss/blob/main/components/c-select.riot
N'hésitez pas à commenter si vous avez des questions ou besoin d'aide concernant RiotJS.
Passez une excellente journée ! Santé 🍻