DEV Community

Cover image for Composant Checkbox avec RiotJS
Steeve
Steeve

Posted on

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) :

<c-checkbox>
   <label class="checkbox" onclick={ inputClick } >
        <input type="checkbox" value={ props?.value ? true : false  } checked={ props?.value } disabled={ props?.disabled }>
        <span>{ props.label }</span>
    </label>
</c-checkbox>
Enter fullscreen mode Exit fullscreen mode

Décomposons le code :

  • Les balises <c-checkbox> et </c-checkbox> 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 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 : <span>{props.label}</span>.

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 :

<input type="checkbox" checked={ null }> devient <input type="checkbox">
<input type="checkbox" checked={ '' }> devient <input type="checkbox">
<input type="checkbox" checked={ false }> devient <input type="checkbox">
Enter fullscreen mode Exit fullscreen mode

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

<input type="checkbox"  checked={ true }> devient <input type="checkbox" checked='checked'>
<input type="checkbox"  checked={ 1 }> devient <input type="checkbox"  checked='checked'>
<input type="checkbox"  checked={ 'is-valid' }> devient <input type="checkbox"  checked='checked'>
Enter fullscreen mode Exit fullscreen mode

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

<index-riot>
    <div style="width:600px;padding:20px;">
        <h4 style="margin-bottom:20px">Riot + BeerCSS</h4>
        <c-checkbox onclick={ clicked } value={ state.value } label={ state.value }/>
        <c-checkbox onclick={ clicked } label="Disabled" disabled={ true } />
        <c-checkbox onclick={ clicked } label="Disabled" disabled={ true } value="true" /> 
    </div>
    <script>
        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 })
                }
            }
        }
    </script>
</index-riot>
Enter fullscreen mode Exit fullscreen mode

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 <c-checkbox onclick={ clicked } />.
  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 <c-checkbox> et sur l'entrée enfant <input type="checkbox">.

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 :

<c-checkbox >
    <label class="checkbox" onclick={ inputClick }>
        <input type="checkbox" value={ props?.value ? true : false  } checked={ props?.value } disabled={ props?.disabled }>
        <span>{ props.label }</span>
    </label>
    <script>
        export default {
            inputClick (e) {
                e.preventDefault();
                e.stopPropagation();
                this.root.value = this.props.value === true || this.props.value === "true" ? false : true;
                this.root.dispatchEvent(new Event('click'));
                this.root.dispatchEvent(new Event('change'));
            }
        }
    </script>
</c-checkbox>
Enter fullscreen mode Exit fullscreen mode

Décomposition du code :

  • Si un clic se produit sur le <label>, 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:

<index-riot>
    <div style="width:600px;padding:20px;">
        <h4 style="margin-bottom:20px">Riot + BeerCSS</h4>
        <c-checkbox onclick={ clicked } value={ state.value } label={ state.value }/>
    </div>
    <script>
        import cCheckbox from "./components/c-checkbox.riot";

        export default {
            components: {
                cCheckbox
            },
            state: {
                value: false
            },
            clicked (ev) {
                this.update({ value: ev.target.value })
            }
        }
    </script>
</index-riot>
Enter fullscreen mode Exit fullscreen mode

Maintenant, state.value prend la valeur de l'événement click, et la valeur reflète toujours l'état actuel de la checkbox.

Conseils pour simplifier davantage : Il n'est pas nécessaire de créer une fonction "clicked", une seule ligne suffit pour mettre à jour la valeur :

<c-checkbox onclick={ (ev) => update({ value: ev.target.value }) } value={ state.value } label={ state.value }/>
Enter fullscreen mode Exit fullscreen mode

Tests du Composant Checkbox

Il existe deux méthodes pour tester le composant de checkbox, couvertes dans deux articles différents :

Conclusion

Voilà 🎉 Nous avons créé un composant Checkbox avec RiotJS en utilisant BeerCSS.

Le code source du composant Checkbox est disponible sur GitHub : https://github.com/steevepay/riot-beercss/blob/main/components/c-checkbox.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)