DEV Community

Cover image for Composant Switch avec RiotJS
Steeve
Steeve

Posted on

Composant Switch avec RiotJS

Cet article traite de la création d'un composant Switch avec RiotJS en utilisant BeerCSS. Avant de commencer, assurez-vous d'avoir une application de base Riot, ou lisez mes articles précédent.

N'hésitez pas à vous référer à la documentation de Riot si nécessaire: https://riot.js.org/documentation/

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

Screenshot of Switch elements made with BeerCSS

Base du Composant Switch

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

Dans ./components/c-switch.riot, écrivez le HTML suivant (Le CSS a été trouvé dans la documentation BeerCSS) :

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

Décomposons le code :

  1. Les balises <c-switch> et </c-switch> 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 les balises HTML natives est une mauvaise pratique, donc commencer par c- est un bon nommage.
  2. Pour activer l'attribut checked, props.value doit être présent et être vrai.
  3. Derrière le switch, il utilise une balise input comme "case à cocher". Note importante: value et checked sont deux attributs différents ; le composant unifie les deux valeurs.
  4. L'élément est désactivé si l'attribut props.disabled existe et que la valeur est true.
  5. Une icône personnalisée peut être affichée sur le switch ; l'attribut HTML props.icon doit exister et il ajoutera une classe icon et une balise <i>icon_name</i>.

Enfin, instanciez le c-switch.riot dans une page index.riot:

<index-riot>
    <div style="width:600px;padding:20px;">
        <h4 style="margin-bottom:20px">Riot + BeerCSS</h4>
        <c-switch  onclick={ clicked } value={ state.value } /><br>
        <c-switch icon="wifi" value={ true } /><br>
        <c-switch icon="bluetooth" disabled={ true } /><br>
        <c-switch icon="dark_mode" disabled={ true } value={ true} /><br>
    </div>
    <script>
        import cSwitch from "./components/c-switch.riot";

        export default {
            components: {
                cSwitch
            },
            state: {
                value: true
            },
            clicked (ev) {
                if (ev.target.tagName === "INPUT") {
                    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 cSwitch from "./components/c-switch.riot"; puis chargé dans l'objet Riot components:{}.
  2. Le composant est instancié avec <c-switch /> dans le HTML. Ajoutez l'attribut "icon" pour afficher une Google Material Icon, comme <c-switch icon="home" />.
  3. L'état du switch est stocké dans l'objet d'état Riot state: { value: true }. "True" est la valeur par défaut.
  4. Pour écouter un événement de clic ou de changement, l'attribut onclick={} et onchange={} doivent être lié à une fonction locale. Dans notre cas, il déclenche la fonction clicked.
  5. Lors du clic, state.value est mis à jour à son opposé avec this.update({ value: !this.state.value }).
  6. Un problème important survient : l'événement de clic est émis deux fois ! L'expression if (ev.target.tagName === "INPUT") accepte un seul événement.

Capture d'écran du HTML généré :
Four switch components made with RiotJS in various states

Corriger le problème du Switch : 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 la balise <c-switch> et sur l'entrée enfant du switch <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 en son opposé : le HTML parent recevra un événement de changement avec la valeur correcte.

  • l'événement change émet true si l'entrée est cochée.
  • l'événement change émet false si l'entrée est décochée.

Le c-switch.riot mis à jour :

<c-switch>
    <label 
        class="
            switch
            { props?.icon ? 'icon' : ''}
        " 
        onclick={ clicked }
    >
        <input type="checkbox" value={ props?.value ? true : false  } checked={ props?.value } disabled={ props?.disabled }>
        <span>
            <i if={ props?.icon }>{ props.icon }</i>
        </span>
    </label>
    <script>
        export default {
            clicked (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-switch>
Enter fullscreen mode Exit fullscreen mode

Décomposition du code :

  • Si un clic se produit sur la balise <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 du switch prend son opposé.
  • Les événements click et change sont réémis grâce à dispatchEvent.

La mise à jour de state.value 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-switch  changed={ changed } value={ state.value } /><br>
    </div>
    <script>
        import cSwitch from "./components/c-switch.riot";

        export default {
            components: {
                cSwitch
            },
            state: {
                value: true
            },
            changed (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 change, et la valeur de l'événement reflète toujours l'état actuel du switch. Enfin, l'événement de clic n'est déclenché qu'une seule fois.

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

<c-switch  changed={ (ev) => update({ value: ev.target.value }) } value={ state.value } /><br>
Enter fullscreen mode Exit fullscreen mode

Test du Composant Switch

Il existe deux méthodes pour tester le composant Switch, et elles sont couvertes dans deux articles différents:

Conclusion

Voilà 🎉 Nous avons créé un composant Switch Riot en utilisant BeerCSS. Le code source du switch est disponible sur Github :
https://github.com/steevepay/riot-beercss/blob/main/components/c-switch.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)