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)