DEV Community

Cover image for 🇫🇷 Input Component avec RiotJS
Steeve
Steeve

Posted on

🇫🇷 Input Component avec RiotJS

Cet article explique comment créer un composant Input avec RiotJS, en utilisant le style Material Design de BeerCSS. Avant de commencer, assurez-vous d'avoir une application de base, ou lisez mon article précédent Configurer Riot + Vite et Riot + BeerCSS.

Ceci est une série d'articles sur RiotJS, pour créer des composants et apprendre les meilleures pratiques pour déployer une application en production. Je pars du principe que vous avez une compréhension de base de Riot ; cependant, n'hésitez pas à consulter la documentation Riot si nécessaire : https://riot.js.org/documentation/

BeerCSS fournit de nombreux états d'entrée, tels que "error", "icons" ou "loading", et il prend en charge différents styles, tels que "round", "small" et "medium". L'objectif est de créer un composant Input qui prend en charge dynamiquement tous les styles et écoute les évènements "input", "change", "onkeyup".

Capture d'écran des input BeerCSS

Base du Component Input

Tout d'abord, créez un nouveau fichier nommé c-input.riot dans le dossier components. Le c- signifie "composant", une convention de nommage pratique:

Dans ./components/c-input.riot, écrivez le HTML et Javascript suivant pour faire un input:

<c-input>
    <div class="field border">
        <input type="text" value="{ props?.value }">
    </div>
</c-input>
Enter fullscreen mode Exit fullscreen mode

Décomposons le code :

  • Pour injecter une valeur personnalisée dans l'input, vous devez utiliser value="{ props?.value }" : La valeur est passée en tant que props. Avec les propriétées Riot, nous pouvons transmettre des données aux composants via des attributs personnalisés.
  • Les balises <c-input> et </c-input> définissent une balise racine personnalisée, portant le même nom que le fichier. Vous devez les écrire ; sinon, cela pourrait créer des résultats inattendus. Utiliser uniquement <input> ou redéfinir des balises HTML natives est une mauvaise pratique.
  • La balise enfant div est copiée des exemples d'input de BeerCSS.

Ensuite, nous pouvons charger et instancier le c-input.riot dans une page d'accueil :

<index-riot>
    <div style="width:500px;padding:20px;">
        <h4 style="margin-bottom:20px">Riot + BeerCSS</h4>
        <c-input value={ state.firstname } onchange={ updateValue } onkeydown={ updateValue }></c-input>
    </div>
    <script>
        import cInput from "../components/c-input.riot";

        export default {
            components: {
                cInput
            },
            state: {
                firstname: 'Default name'
            },
            updateValue (ev) {
                this.update({
                    firstname: ev.target.value
                })
            }
        }
    </script>
</index-riot>
Enter fullscreen mode Exit fullscreen mode

Analyse du code :

  1. Le composant est importé avec import cInput from "../components/c-input.riot";, puis chargé dans l'objet Riot components:{}.
  2. Dans le HTML, le composant Input est instancié avec <c-input value={ state.firstname } onchange={ updateValue } onkeydown={ updateValue }></c-input>.
  3. Trois attributs sont transmis :
    • value : ce sera la valeur de l'input.
    • onchange et onkeydown : Ce sont des événements émis si la valeur de l'input change ou si une touche est pressée. Lorsqu'un événement est déclenché, il exécute une fonction, dans notre cas updateValue.
  4. La valeur par défaut du firstname est "Default name".

Pour stocker la valeur de l'input, un état nommé firstname est créé: state.firstname, et il doit être mis à jour grâce à la fonction nommée updateValue. Si la valeur de l'input change, le firstname prendra la nouvelle valeur de l'input, puis la propagera dans props.

Écrire une fonction dédiée pour mettre à jour state.firstname n'est pas nécessaire, cela peut être simplifié en une seule ligne:

<c-input value={ state.firstname } onchange={ (ev) => update({ firstname: ev.target.value }) } onkeydown={ (ev) => update({ firstname: ev.target.value }) }></c-input>
Enter fullscreen mode Exit fullscreen mode

Supporter les Styles du Composant Input

Un input peut afficher plusieurs styles : normal, helper, errors, icon, chargement, ou tous combinés.

Ajoutons le "helper", un message d'aide affiché sous l'Input:

<c-input>
    <div class="field border">
        <input type="text" value={ props?.value ?? '' }>
        <span class="helper" if={ props?.helper }>{ props.helper }</span>
    </div>
</c-input>
Enter fullscreen mode Exit fullscreen mode

Le HTML provient de BeerCSS, puis j'ai simplement ajouté deux éléments :

  • { props.helper } Il affichera l'aide via les props (attribut HTML).
  • if={ props?.helper } condition pour afficher ou masquer le message d'aide si une valeur existe.

Essayons l'aide en définissant l'attribut "helper" avec une chaîne, par exemple :

<c-input helper="This is a helper" label="Firstname" value={ state.firstname } onchange={ (ev) => update({ firstname: ev.target.value }) } onkeydown={ (ev) => update({ firstname: ev.target.value }) }></c-input>        
Enter fullscreen mode Exit fullscreen mode

Le résultat:

Riot input component with a helper as attribute

Si l'attribut "helper" est vide, il masquera les balises HTML "span".

L'expression est la même pour un label et une erreur:

<c-input>
    <div class="
        field border
        { props?.label ? ' label' : '' }
    ">
        <input type="text" value={ props?.value }>
        <label if={ props?.label }>{ props.label }</label>
        <span class="helper" if={ props?.helper && !props?.error }>{ props.helper }</span>
        <span class="error" if={ props?.error }>{ props.error }</span>
    </div>
</c-input>
Enter fullscreen mode Exit fullscreen mode

Analyse du code :

  • Riot supporte les conditions Javascripts dans les valeurs des attributs, dans notre cas nous ajoutons la classe label si props.label existe.
  • La balise et la valeur du label sont affichées si props.label existe.
  • L'erreur est imprimée uniquement si props.error existe. En même temps, la condition masque "props.helper" parce que c'est plus important d'afficher une erreur.

Riot Input Component with an error state

Nous pouvons suivre la même logique pour prendre en charge l'icone de chargement, les icônes en préfixe (props.input) et suffixe (props.inputend):

<c-input>
    <div class="
        field border
        { props?.error ? " invalid" : '' }
        { props?.label ? " label" : '' }
        { props?.icon || props?.loading ? " prefix" : '' }
        { props?.iconend || props?.loadingend  ? " suffix" : '' }
    ">
        <progress if={ props?.loading } class="circle"></progress>
        <i if={ props?.icon && !props?.loading }>{ props?.icon }</i>
        <input type="text" value={ props?.value }>
        <label if={ props?.label }>{ props.label }</label>
        <i if={ props?.iconend && !props?.loadingend }>{ props?.iconend }</i>
        <progress if={ props?.loadingend } 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-input>
Enter fullscreen mode Exit fullscreen mode

De nombreuses applications ont besoin de différents types d'Input, tels que des nombres, des mots de passe, et plus encore. Créons une prop pour prendre en charge tous les types:

<input type={props?.type ?? 'text'} value={ props?.value }> 
Enter fullscreen mode Exit fullscreen mode

Le type d'Input par défaut est text, et il peut également prendre en charge : number, password, file, color, date, time.

Le composant est presque terminé: Ajoutons maintenant la prise en charge de plusieurs tailles et formes (comme rond, remplissage gris, petit, etc.). Nous devons ajouter des classes conditionnellement:

        { props?.round ? " round" : ''}
        { props?.fill ? " fill" : ''}
        { props?.small ? " small" : ''}
        { props?.medium ? " medium" : ''}
        { props?.large ? " large" : ''}
        { props?.extra ? " extra" : ''}
Enter fullscreen mode Exit fullscreen mode

Voici le code final du composant Input :

<c-input>
    <div class="
        field border
        { props?.round ? " round" : ''}
        { props?.fill ? " fill" : ''}
        { props?.small ? " small" : ''}
        { props?.medium ? " medium" : ''}
        { props?.large ? " large" : ''}
        { props?.extra ? " extra" : ''}
        { props?.error ? " invalid" : '' }
        { props?.label ? " label" : '' }
        { props?.icon || props?.loading ? " prefix" : '' }
        { props?.iconend || props?.loadingend  ? " suffix" : '' }
    ">
        <progress if={ props?.loading } class="circle"></progress>
        <i if={ props?.icon && !props?.loading }>{ props?.icon }</i>
        <input type={props?.type ?? 'text'} value={ props?.value }>
        <label if={ props?.label }>{ props.label }</label>
        <i if={ props?.iconend && !props?.loadingend }>{ props?.iconend }</i>
        <progress if={ props?.loadingend } 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-input>
Enter fullscreen mode Exit fullscreen mode

Enfin, nous pouvons instancier dans le fichier index.riot plusieurs <c-inputs> pour essayer tous les attributs :

<c-input label="Firstname" value={ state.firstname } onchange={ (ev) => update({ firstname: ev.target.value }) } onkeyup={ (ev) => update({ firstname: ev.target.value }) }></c-input>

<c-input helper="This is a helper" label="Firstname" value={ state.firstname } onchange={ (ev) => update({ firstname: ev.target.value }) } onkeyup={ (ev) => update({ firstname: ev.target.value }) }></c-input>        

<c-input error="Something is wrong" helper="This is a helper" label="Firstname" value={ state.firstname } onchange={ (ev) => update({ firstname: ev.target.value }) } onkeyup={ (ev) => update({ firstname: ev.target.value }) }></c-input>        

<c-input icon="edit" label="Firstname" value={ state.firstname } onchange={ (ev) => update({ firstname: ev.target.value }) } onkeyup={ (ev) => update({ firstname: ev.target.value }) }></c-input>

<c-input iconend="edit" label="Firstname" value={ state.firstname } onchange={ (ev) => update({ firstname: ev.target.value }) } onkeyup={ (ev) => update({ firstname: ev.target.value }) }></c-input>

<c-input icon="search" iconend="edit" label="Firstname" value={ state.firstname } onchange={ (ev) => update({ firstname: ev.target.value }) } onkeyup={ (ev) => update({ firstname: ev.target.value }) }></c-input>

<c-input loading="true" icon="edit" label="Firstname" value={ state.firstname } onchange={ (ev) => update({ firstname: ev.target.value }) } onkeyup={ (ev) => update({ firstname: ev.target.value }) }></c-input>

<c-input loadingend="true" iconend="edit" label="Firstname" value={ state.firstname } onchange={ (ev) => update({ firstname: ev.target.value }) } onkeyup={ (ev) => update({ firstname: ev.target.value }) }></c-input>

<c-input fill="true" round="true" small="true" label="Test" value={ state.firstname } onchange={ (ev) => update({ firstname: ev.target.value }) } onkeyup={ (ev) => update({ firstname: ev.target.value }) }></c-input>
Enter fullscreen mode Exit fullscreen mode

Voici le résutat:
Image description

Le code source de l'Input est disponible sur Github:
https://github.com/steevepay/riot-beercss/blob/main/components/c-input.riot

Conclusion

Et voilà 🎉 Nous avons créé un composant Input avec RiotJS en utilisant un style Material Design avec BeerCSS.

N'hésitez pas à commenter si vous avez des questions ou besoin d'aide concernant RiotJS.

Bonne journée ! Cheers 🍻

Top comments (0)