Composant Table avec RiotJS
Cet article traite de la création d'un composant Table avec RiotJS, en utilisant BeerCSS. Avant de commencer, assurez-vous d'avoir une application de base RiotJS, ou lisez mes articles précédents. n'hésitez pas à consulter la documentation de Riot si nécessaire : https://riot.js.org/documentation/ Une Table est un élément complexe et riche avec de nombreux styles et actions. L'article vise à créer un composant Table avec un design BeerCSS, affichant une liste d'objets. Ensuite, personnalisez les cellules avec des éléments spéciaux (chips, cases à cocher, etc.). Base du composant Table Tout d'abord, créez un nouveau fichier nommé c-table.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 dans ./components/c-table.riot (le CSS a été trouvé dans la documentation BeerCSS): { col } { val } { header } thead, tfoot > tr > th::first-letter { text-transform:capitalize; } Décomposons le code : Les balises et 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 comme balise racine ou redéfinir des balises HTML natives est une mauvaise pratique, donc commencer par c- est un bon nommage. Le composant peut recevoir une liste d'objets items en tant qu'attribut, et peut être utilisé dans le composant avec props?.items. Pour créer des cellules, l'expression Riot each est utilisée pour parcourir les éléments : Une première boucle est utilisée pour chaque balise de ligne de tableau avec . L'item est un objet de la liste. Une deuxième boucle enfant est utilisée pour chaque cellule de tableau avec { val }. L'expression Object.values() renvoie un tableau des valeurs d'un objet donné. Pour l'en-tête du tableau, il utilise les noms de key du premier élément de la liste props.items : La fonction Object.keys est utilisée pour renvoyer un tableau de toutes les clés d'objet de l'élément donné, comme { col }. BeerCSS permet la création d'un pied de page, qui suit la même logique que l'en-tête : accéder au premier élément de la liste, obtenir une liste de clés avec Object.keys et créer une boucle avec l'expression Riot each. Le pied de page est affiché conditionnellement si la propriété props.footer existe. La première lettre de chaque en-tête est capitalisée grâce à l'expression CSS : th::first-letter { text-transform:capitalize; }. Enfin, BeerCSS fournit des classes utiles pour changer le style du tableau, par exemple, ajouter des rayures ou des bordures ou changer l'alignement. Les classes sont injectées conditionnellement avec l'expression Riot : { props?.stripes ? 'stripes ' : null } signifiant que si la propriété props.stripes existe, la classe stripes est ajoutée. Enfin, chargez et instanciez le c-table.riot dans une page nommée index.riot : Riot + BeerCSS import cTable from "./components/c-table-full.riot" export default { components: { cTable }, state: { animals: [ { id: 1, name: 'African Elephant', species: 'Loxodonta africana', diet: 'Herbivore', habitat: 'Savanna, Forests', enabled: false }, { id: 2, name: 'Lion', species: 'Panthera leo', diet: 'Carnivore', habitat: 'Savanna, Grassland', enabled: true }, { id: 3, name: 'Hippopotamus', species: 'Hippopotamus amphibius', diet: 'Herbivore', habitat: 'Riverbanks, Lakes', enabled: false } ] } } Détails du code : Les composants sont importés avec import cTable from "./components/c-table.riot"; puis chargés dans l'objet Riot components:{}. Le tableau est instancié avec dans le HTML. Une liste d'animaux est fournie au tableau, grâce à l'attribut items : items={ state.animals } ; le tableau créera automatiquement les en-têtes, les lignes et les cellules ! ✅ Voici le HTML généré : Composant Table avec cellules personnalisées Afficher des composants personnalisés dans les cellules est souvent nécessaire pour les applications de producti

Cet article traite de la création d'un composant Table avec RiotJS, en utilisant BeerCSS. Avant de commencer, assurez-vous d'avoir une application de base RiotJS, ou lisez mes articles précédents.
n'hésitez pas à consulter la documentation de Riot si nécessaire : https://riot.js.org/documentation/
Une Table est un élément complexe et riche avec de nombreux styles et actions. L'article vise à créer un composant Table avec un design BeerCSS, affichant une liste d'objets. Ensuite, personnalisez les cellules avec des éléments spéciaux (chips, cases à cocher, etc.).
Base du composant Table
Tout d'abord, créez un nouveau fichier nommé c-table.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 dans ./components/c-table.riot
(le CSS a été trouvé dans la documentation BeerCSS):
class="
{ props?.leftAlign ? 'left-align ' : null }
{ props?.centerAlign ? 'center-align ' : null }
{ props?.rightAlign ? 'right-align ' : null }
{ props?.stripes ? 'stripes ' : null }
{ props?.outlined ? 'border ' : null }
{ props?.scroll ? 'scroll' : null }"
>
class="{props?.scroll ? ' fixed' : null }">
each={ col in Object.keys(props?.items?.[0])}>{ col }
each={ item in props?.items}>
if={ item } each={ val in Object.values(item) }>
{ val }
if={ props?.footer === true } class="{props?.scroll ? ' fixed' : null }">
each={ header in Object.keys(props?.items?.[0])}>{ header }
thead, tfoot > tr > th::first-letter {
text-transform:capitalize;
}
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. - Le composant peut recevoir une liste d'objets items en tant qu'attribut, et peut être utilisé dans le composant avec
props?.items
. - Pour créer des cellules, l'expression Riot each est utilisée pour parcourir les éléments :
- Une première boucle est utilisée pour chaque balise de ligne de tableau avec
. L'item est un objet de la liste. - Une deuxième boucle enfant est utilisée pour chaque cellule de tableau avec
. L'expression{ val } Object.values()
renvoie un tableau des valeurs d'un objet donné.- Pour l'en-tête du tableau, il utilise les noms de
key
du premier élément de la listeprops.items
: La fonctionObject.keys
est utilisée pour renvoyer un tableau de toutes les clés d'objet de l'élément donné, comme
.{ col } - BeerCSS permet la création d'un pied de page, qui suit la même logique que l'en-tête : accéder au premier élément de la liste, obtenir une liste de clés avec
Object.keys
et créer une boucle avec l'expression Riot each. Le pied de page est affiché conditionnellement si la propriétéprops.footer
existe.- La première lettre de chaque en-tête est capitalisée grâce à l'expression CSS :
th::first-letter { text-transform:capitalize; }
.- Enfin, BeerCSS fournit des classes utiles pour changer le style du tableau, par exemple, ajouter des rayures ou des bordures ou changer l'alignement. Les classes sont injectées conditionnellement avec l'expression Riot :
{ props?.stripes ? 'stripes ' : null }
signifiant que si la propriétéprops.stripes
existe, la classestripes
est ajoutée.Enfin, chargez et instanciez le c-table.riot dans une page nommée index.riot :
style="width:800px;padding:20px;">import cTable from "./components/c-table-full.riot" export default { components: { cTable }, state: { animals: [ { id: 1, name: 'African Elephant', species: 'Loxodonta africana', diet: 'Herbivore', habitat: 'Savanna, Forests', enabled: false }, { id: 2, name: 'Lion', species: 'Panthera leo', diet: 'Carnivore', habitat: 'Savanna, Grassland', enabled: true }, { id: 3, name: 'Hippopotamus', species: 'Hippopotamus amphibius', diet: 'Herbivore', habitat: 'Riverbanks, Lakes', enabled: false } ] } }style="margin-bottom:20px">Riot + BeerCSS
items={ state.animals } /> Détails du code :
- Les composants sont importés avec
import cTable from "./components/c-table.riot";
puis chargés dans l'objet Riotcomponents:{}
. - Le tableau est instancié avec
dans le HTML. - Une liste d'animaux est fournie au tableau, grâce à l'attribut items :
items={ state.animals } ;
le tableau créera automatiquement les en-têtes, les lignes et les cellules ! ✅
Composant Table avec cellules personnalisées
Afficher des composants personnalisés dans les cellules est souvent nécessaire pour les applications de production, comme des cases à cocher, ou même des boutons.
Cette capacité est activée grâce aux Slots: La balise
injecte des modèles HTML personnalisés dans un composant enfant depuis son parent. Modifions la boucle suivante dansc-table.riot
:
each={ val in Object.values(item) }> { val } Remplacez-la par l'expression suivante :
each={ el in Object.entries(item) }> name="item" item={ item } column={ el?.[0] } value={ el?.[1] }> if={ slots.length === 0 }> { el?.[1] }Expliquons ce qui se passe ici :
- Au lieu d'utiliser
Object.values
, on utiliseObject.entries
pour convertir un objet en une liste de[key, value]
. Par exemple,[{key: 1, name: "blue"}]
renvoie[["key", 1], ["name", "blue"]]
. - Un slot est créé pour chaque cellule :
. - Passer des valeurs dynamiques aux slots s'appelle des Higher Order Components: Tous les attributs définis sur les balises de slot seront disponibles dans leurs modèles HTML injectés. Dans notre cas, quatre attributs sont créés :
- Le slot a un nom optionnel, une convention de nommage Riot, sans le définir, il aurait le nom
default
. Il n'est pas possible de définir le nom dynamiquement. - L'objet
item
de la boucle de ligne est passé dans l'attributitem
du slot : Il sera accessible sur le composant passé en tant que slots !. - La
clé
de l'élément est passée à l'attribut column du slot grâce àcolumn={ el?.[0] }
. - La
valeur
de l'élément est passée à l'attribut value du slot grâce àvalue={ el?.[1] }
.
- Le slot a un nom optionnel, une convention de nommage Riot, sans le définir, il aurait le nom
- Si le slot n'existe pas, la valeur de la cellule est imprimée avec
{ el?.[1] }
. Les directivesif
peuvent être utilisées avec une balise, dans notre cas, on souhaite afficher conditonnellement la valeur de l'élément (en savoir plus sur la documentation Riot).
Maintenant, dans index.riot, affichons un composant
Chip
pour la colonne Diet :
style="width:800px;padding:20px;">import cTable from "./components/c-table-full.riot" import cChip from "./components/c-chip.riot" export default { components: { cTable, cChip }, state: { animals: [ { id: 1, name: 'African Elephant', species: 'Loxodonta africana', diet: 'Herbivore', habitat: 'Savanna, Forests', enabled: false }, { id: 2, name: 'Lion', species: 'Panthera leo', diet: 'Carnivore', habitat: 'Savanna, Grassland', enabled: true }, { id: 3, name: 'Hippopotamus', species: 'Hippopotamus amphibius', diet: 'Herbivore', habitat: 'Riverbanks, Lakes', enabled: false } ] } }style="margin-bottom:20px">Riot + BeerCSS
items={ state.animals }> slot="item"> if={ column === 'diet' }> { value } if={ column !== 'diet' }> { value }Décomposition du code :
- Le composant Chip est définie dans l'objet Riot
components:{}
puis chargée avec
.Label - Dès que nous utilisons
slot="item"
, par exemple
, l'élément Chip sera injecté dans toutes les cellules. Utiliser une seule Chip comme Slot est une mauvaise idée : la Chip sera affichée pour toutes les cellules. Ce n'est pas ce que nous voulons !{ value } - Grâce aux trois attributs dynamiques (magie des Higher Order Components ✨), nous pouvons contrôler ce qui est affiché :
-
item
est l'objet pour la ligne actuelle, -
column
pour la clé d'en-tête actuelle, -
value
pour la valeur de la cellule.
-
- Lorsque plusieurs éléments HTML sont affichés dans un slot, il est préférable de les envelopper dans une balise "template", dans notre cas :
.
- La partie la plus importante est :
. Si la colonne actuelle est diet, la Chip affiche la valeur, sinon, la valeur brute est affichée.{ value } { value }
Voici le résultat HTML :
Composant Table avec des cases à cocher
Le processus d'insertion de cases à cocher dans le tableau est le même que dans la section précédente : le composant de case à cocher est passé en tant que Slot, et il doit être affiché en fonction d'un nom de colonne.
Dans notre cas, la case à cocher obtient la valeur booléenne de la colonne
enabled
.Voici le code HTML pour index.riot:
style="width:800px;padding:20px;">import cTable from "./components/c-table-full.riot" import cChip from "./components/c-chip.riot" import cCheckbox from "./components/c-checkbox.riot" export default { changed (id, value) { const _el = this.state.animals.find(el => el.id === id); if (_el) { _el.enabled = value; this.update(); } }, components: { cTable, cChip, cCheckbox }, state: { animals: [ { id: 1, name: 'African Elephant', species: 'Loxodonta africana', diet: 'Herbivore', habitat: 'Savanna, Forests', enabled: false }, { id: 2, name: 'Lion', species: 'Panthera leo', diet: 'Carnivore', habitat: 'Savanna, Grassland', enabled: true }, { id: 3, name: 'Hippopotamus', species: 'Hippopotamus amphibius', diet: 'Herbivore', habitat: 'Riverbanks, Lakes', enabled: false } ] } }style="margin-bottom:20px">Riot + BeerCSS
items={ state.animals }> slot="item"> if={ column === 'enabled' } value={ value } onchange={ (ev) => changed( item.id, ev.target.value ) } /> if={ column === 'diet' }> { value } if={ column !== 'diet' && column !== 'enabled' }> { value }Décomposition du code :
- La case à cocher personnalisée est définie comme un composant dans l'objet Riot
components:{}
puis chargée avec
. - La case à cocher est imprimée uniquement si la colonne actuelle est
enabled
. - La valeur de la case à cocher est définie grâce à
value={ value }
. - Si un clic se produit sur la case à cocher, l'événement
change
est émis, et la fonctionchanged()
est exécutée avec deux attributsonchange={ (ev) => changed( item.id, ev.target.value ) }
:- La fonction obtient la valeur d'ID de l'élément comme premier argument.
- En tant que deuxième argument, la fonction obtient la valeur de la case à cocher.
- La fonction changed est exécutée : d'abord, l'objet actuel est trouvé grâce à l'ID de l'élément, et enfin la valeur
enabled
est mise à jour.
Capture d'écran du tableau généré avec des cases à cocher :
Test du composant Table
Il existe deux méthodes pour tester le composant Table, 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à
- Une deuxième boucle enfant est utilisée pour chaque cellule de tableau avec
- Une première boucle est utilisée pour chaque balise de ligne de tableau avec