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:
<c-select>
<div class="field suffix
{props?.label ? ' label' : null }
{props?.error ? ' invalid' : null }
">
<select>
<option if={ !props?.loading } each={option in props.options} value={ option } selected={ option === props.value ? true : null }>{ option }</option>
</select>
<label if={ props?.label }>{ props.label }</label>
<i if={ !props?.loading && !props?.img && !props?.icon} >arrow_drop_down</i>
<i if={ !props?.loading && !props?.img && props?.icon} >{ props.icon }</i>
<img if={ !props?.loading && props?.img } class="circle" src={ props.img }>
<progress if={ props?.loading } class="circle"></progress>
<span class="helper" if={ props?.helper && !props?.error }>{ props.helper }</span>
<span class="error" if={ props?.error }>{ props.error }</span>
</div>
</c-select>
Décomposons le code :
- Les balises
<c-select>
et</c-select>
dé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<label>
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 balise<option>
est 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
<option>
:- 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:
<index-riot>
<div style="width:600px;padding:20px;">
<h4 style="margin-bottom:20px">Riot + BeerCSS</h4>
<c-select label="Color" options={ state.options } value={ state.value } onchange={ changed } />
<c-select img="./favicon.png" helper={ "Color selected: " + state.value } label="Color" options={ state.options } value={ state.value } onchange={ changed }/>
</div>
<script>
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 })
}
}
</script>
</index-riot>
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<c-select/>
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 })
.
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 :
<option 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] }</option>
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 :
<index-riot>
<div style="width:600px;padding:20px;">
<h4 style="margin-bottom:20px">Riot + BeerCSS</h4>
<c-select 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 }/>
</div>
<script>
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)})
}
}
</script>
</index-riot>
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.
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é 🍻
Top comments (0)