Composant Checkbox avec RiotJS

Cet article traite de la création d'un composant de Checkbox (case à cocher) avec RiotJS, en utilisant le CSS Material Design BeerCSS. Avant de commencer, assurez-vous d'avoir une application de base, ou lisez mes articles précédents de la série. N'hésitez pas à consulter la documentation de RiotJS si nécessaire: https://riot.js.org/documentation/ Quatre états de checkbox existent : non coché, coché et désactivé, et mixte (voir la capture d'écran suivante). L'objectif est de créer un composant de checkbox Riot avec le design BeerCSS et d'écouter les événements de clic. Composant Checkbox de Base Tout d'abord, créez un nouveau fichier nommé c-checkbox.riot dans le dossier des composants. Le préfixe c- signifie "composant", une convention de nommage utile et une bonne pratique. Dans ./components/c-checkbox.riot, écrivez le code HTML suivant (trouvé dans la documentation BeerCSS) : { props.label } 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 choix de nom. Pour activer l'attribut checked, props.value doit exister et être true. La valeur par défaut d'une entrée de la checkbox est "on" sous forme de chaîne. Ce n'est pas pratique à manipuler ce genre de valeur dans une application JavaScript. L'expression value={ props?.value ? true : false } garantit que la valeur de la checkbox est toujours un booléen, soit true, soit false. Selon les normes Web pour une checkbox, La value et checked sont deux attributs différents ; le composant unifie les deux attributs. Grâce à props.disabled, l'attribut disabled est conditionnellement assigné à la balise Input pour désactiver la checkbox. Si le label existe, il est injecté dans : {props.label}. Le W3C stipule qu'une propriété booléenne est vraie si l'attribut est présent — même si la valeur est vide ou fausse. Comme mentionné dans la documentation, Riot.js corrige automatiquement ce comportement : les attributs booléens (checked, selected, etc.) sont ignorés lorsque la valeur de l'expression est false : devient devient devient Dans le cas où l'expression est true, ils seront correctement rendus selon les spécifications : devient devient devient Enfin, le composant c-checkbox.riot peut être instancié dans une page d'accueil index.riot : Riot + BeerCSS import cCheckbox from "./components/c-checkbox.riot"; export default { components: { cCheckbox }, state: { value: true }, clicked (ev) { if (ev.target.tagName === "SPAN") { ev.stopPropagation() ev.preventDefault(); this.update({ value: !this.state.value }) } } } Décomposition du code : Le composant est importé avec import cCheckbox from "./components/c-checkbox.riot"; puis chargé dans l'objet Riot components:{}. Dans le HTML, le composant checkbox est instancié avec . L'état de la checkbox est stocké dans l'objet Riot state state: { value: false }. False est la valeur par défaut. L'événement click est surveillé : lorsque l'événement click est déclenché, la fonction clicked est exécutée. Lors du clic, la value est mise à jour à son opposé avec this.update({ value: !this.state.value }). Un problème important survient : l'événement click est émis deux fois ! L'expression if (ev.target.tagName === "SPAN") est utilisée pour accepter un seul événement, puis la propagation de l'événement est arrêtée grâce à ev.stopPropagation(); et ev.preventDefault();. Capture d'écran du HTML généré : Corriger le problème de la checkbox : arrêter l'événement de double-clic Comme mentionné dans la section précédente, l'événement click est déclenché deux fois. Le problème est que cliquer sur l'étiquette déclenche un clic à la fois sur et sur l'entrée enfant . La solution consiste à arrêter la propagation de l'événement à l'intérieur du composant et à réémettre l'événement une seule fois. À ce moment-là, je prends l'opportunité de changer la valeur booléenne à son opposé : le HTML parent recevra un événement de changement avec la valeur correcte : Si l'entrée est cochée, l'événement de changement émet true. Si l'entrée est décochée, l'événement de changement émet false. Le composant c-checkbox.riot mis à jour : { props.label } export default { inputClick (e) { e.preventDefault(); e.stopPropagation(); this.root.value = this.props.value === true || this.props.value === "true" ?

Mar 10, 2025 - 13:24
 0
Composant Checkbox avec RiotJS

Cet article traite de la création d'un composant de Checkbox (case à cocher) avec RiotJS, en utilisant le CSS Material Design BeerCSS. Avant de commencer, assurez-vous d'avoir une application de base, ou lisez mes articles précédents de la série.

N'hésitez pas à consulter la documentation de RiotJS si nécessaire: https://riot.js.org/documentation/

Quatre états de checkbox existent : non coché, coché et désactivé, et mixte (voir la capture d'écran suivante). L'objectif est de créer un composant de checkbox Riot avec le design BeerCSS et d'écouter les événements de clic.

Screenshot of BeerCSS Checkboxes

Composant Checkbox de Base

Tout d'abord, créez un nouveau fichier nommé c-checkbox.riot dans le dossier des composants. Le préfixe c- signifie "composant", une convention de nommage utile et une bonne pratique.

Dans ./components/c-checkbox.riot, écrivez le code HTML suivant (trouvé dans la documentation BeerCSS) :


    class="checkbox" onclick={ inputClick } >
         type="checkbox" value={ props?.value ? true : false  } checked={ props?.value } disabled={ props?.disabled }>
        { props.label }
    

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 choix de nom.
  • Pour activer l'attribut checked, props.value doit exister et être true.
  • La valeur par défaut d'une entrée de la checkbox est "on" sous forme de chaîne. Ce n'est pas pratique à manipuler ce genre de valeur dans une application JavaScript. L'expression value={ props?.value ? true : false } garantit que la valeur de la checkbox est toujours un booléen, soit true, soit false.
  • Selon les normes Web pour une checkbox, La value et checked sont deux attributs différents ; le composant unifie les deux attributs.
  • Grâce à props.disabled, l'attribut disabled est conditionnellement assigné à la balise Input pour désactiver la checkbox.
  • Si le label existe, il est injecté dans : {props.label}.

Le W3C stipule qu'une propriété booléenne est vraie si l'attribut est présent — même si la valeur est vide ou fausse. Comme mentionné dans la documentation, Riot.js corrige automatiquement ce comportement : les attributs booléens (checked, selected, etc.) sont ignorés lorsque la valeur de l'expression est false :

 type="checkbox" checked={ null }> devient  type="checkbox">
 type="checkbox" checked={ '' }> devient  type="checkbox">
 type="checkbox" checked={ false }> devient  type="checkbox">

Dans le cas où l'expression est true, ils seront correctement rendus selon les spécifications :

 type="checkbox"  checked={ true }> devient  type="checkbox" checked='checked'>
 type="checkbox"  checked={ 1 }> devient  type="checkbox"  checked='checked'>
 type="checkbox"  checked={ 'is-valid' }> devient  type="checkbox"  checked='checked'>

Enfin, le composant c-checkbox.riot peut être instancié dans une page d'accueil index.riot :


     style="width:600px;padding:20px;">
         style="margin-bottom:20px">Riot + BeerCSS
         onclick={ clicked } value={ state.value } label={ state.value }/>
         onclick={ clicked } label="Disabled" disabled={ true } />
         onclick={ clicked } label="Disabled" disabled={ true } value="true" /> 
    

Décomposition du code :

  1. Le composant est importé avec import cCheckbox from "./components/c-checkbox.riot"; puis chargé dans l'objet Riot components:{}.
  2. Dans le HTML, le composant checkbox est instancié avec .
  3. L'état de la checkbox est stocké dans l'objet Riot state state: { value: false }. False est la valeur par défaut.
  4. L'événement click est surveillé : lorsque l'événement click est déclenché, la fonction clicked est exécutée.
  5. Lors du clic, la value est mise à jour à son opposé avec this.update({ value: !this.state.value }).
  6. Un problème important survient : l'événement click est émis deux fois ! L'expression if (ev.target.tagName === "SPAN") est utilisée pour accepter un seul événement, puis la propagation de l'événement est arrêtée grâce à ev.stopPropagation(); et ev.preventDefault();.

Capture d'écran du HTML généré :

Four different checkbox components: default with label, disabled, disabled and checked

Corriger le problème de la checkbox : arrêter l'événement de double-clic

Comme mentionné dans la section précédente, l'événement click est déclenché deux fois. Le problème est que cliquer sur l'étiquette déclenche un clic à la fois sur et sur l'entrée enfant .

La solution consiste à arrêter la propagation de l'événement à l'intérieur du composant et à réémettre l'événement une seule fois. À ce moment-là, je prends l'opportunité de changer la valeur booléenne à son opposé : le HTML parent recevra un événement de changement avec la valeur correcte :

  • Si l'entrée est cochée, l'événement de changement émet true.
  • Si l'entrée est décochée, l'événement de changement émet false.

Le composant c-checkbox.riot mis à jour :

 >
     class="checkbox" onclick={ inputClick }>
         type="checkbox" value={ props?.value ? true : false  } checked={ props?.value } disabled={ props?.disabled }>
        { props.label }
    
    

Décomposition du code :

  • Si un clic se produit sur le , l'événement de clic n'est pas propagé et est annulé, grâce à e.preventDefault(); et e.stopPropagation();.
  • La valeur de l'entrée de la checkbox prend son opposé.
  • Les événements click et change sont réémis grâce à dispatchEvent.

La mise à jour de la valeur sur le composant parent index.riot peut être simplifiée:


     style="width:600px;padding:20px;">
         style="margin-bottom:20px">Riot + BeerCSS
         onclick={ clicked } value={ state.value } label={ state.value }/>