DEV Community

Cover image for Composant Recherche avec RiotJS
Steeve
Steeve

Posted on

Composant Recherche avec RiotJS

Cet article traite de la création d'un composant de recherche (Search) avec Riot, en utilisant le CSS Material Design BeerCSS, et de l'exécution d'une action lors des événements d'entrée et de sélection.

Avant de commencer, assurez-vous d'avoir une application de base RiotJS, ou consultez mes articles précédents.

La recherche permet aux utilisateurs d'entrer un mot-clé ou une phrase pour obtenir des informations pertinentes : Les utilisateurs saisissent une requête dans la barre de recherche (1) et voient ensuite les résultats associés (2).

Image description

Base du Composant de Recherche

L'objectif est de créer une application Riot avec une barre de recherche, d'afficher les résultats de recherche, et d'exécuter une action lorsqu'un résultat est sélectionné.

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

Écrivez le code suivant dans ./components/c-search.riot. Le HTML provient de la documentation BeerCSS et j'ai ajouté la syntaxe RiotJS pour la logique :

<c-search>
    <div class="field prefix
        { props?.outlined ? " border" : ''}
        { props?.round ? " round" : ''}
        { props?.fill ? " fill" : ''}
        { props?.small ? " small" : ''}
        { props?.medium ? " medium" : ''}
        { props?.large ? " large" : ''}
        { props?.extra ? " extra" : ''}
        { props?.error ? " invalid" : '' }
        ">
        <progress if={ props?.loading } class="circle"></progress>
        <i if={ !props?.loading } class="front">{ props?.iconSearch ?  props.iconSearch : "search"}</i>
        <input type="text" value={ props?.value } placeholder={ props?.placeholder } />
        <menu class="{ props?.max ? "max" : "min" }">
            <div class="field prefix suffix no-margin fixed         
                            { props?.outlined ? " border" : ''}
                            { props?.round ? " round" : ''}
                            { props?.fill ? " fill" : ''}
                            { props?.small ? " small" : ''}
                            { props?.medium ? " medium" : ''}
                            { props?.large ? " large" : ''}
                            { props?.extra ? " extra" : ''}
                            { props?.error ? " invalid" : '' }
                        ">
                <i class="front">{ props?.iconBack ?  props.iconBack : "arrow_back"}</i>
                <input type="text" value={ props?.value } placeholder={ props?.placeholder } />
                <i onclick={ clear } class="front">{ props?.iconClose ?  props.iconClose : "close"}</i>
            </div>
            <a class="row" each={res in props?.results} onclick={ (ev) => select(ev, res) }>
                <i>{ res?.icon ?? props?.iconResult ?? 'history' }</i>
                <div>{ res?.label ?? res?.name ?? res }</div>
            </a>
        </menu>
    </div>
    <script>
        export default {
            select(e, result) {
                e.preventDefault();
                e.stopPropagation();
                this.root.value = result;
                this.root.dispatchEvent(new Event('select'));
            },
            clear (e) {
                e.preventDefault();
                e.stopPropagation();
                this.root.dispatchEvent(new Event('clear'));
            }
        }
    </script>
</c-search>
Enter fullscreen mode Exit fullscreen mode

Source Code: https://github.com/steevepay/riot-beercss/blob/main/components/c-search.riot

Expliquons le code :

  1. Les balises <c-search> et </c-search> 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 <div></div> comme balise racine ou redéfinir des balises HTML natives est une mauvaise pratique, donc commencer par c- est une bonne convention.
  2. La recherche a deux états :
    • État par défaut : Sans interactions, la barre de recherche principale est un champ de recherche persistant et visible en haut de l'écran : le premier élément HTML <input> est affiché.
    • État de focus : Lors du focus, la barre de recherche s'étend en une vue de recherche, affichant des suggestions de recherche historique : Le second élément HTML <input> est affiché à l'intérieur d'un élément <menu> suivi d'une liste de résultats.
  3. La liste des résultats est affichée grâce à une expression Each de Riot : <a class="row" each={res in props?.results}></a>. Le résultat affiche une icône et le nom. Le résultat peut être un tableau d'objets ou un tableau de chaînes ; le nom est alors affiché grâce à la condition : { res?.label ?? res?.name ?? res }. Il prend d'abord la propriété label, puis name ; sinon, il affiche res comme une chaîne.
  4. Si un clic se produit sur l'un des résultats, la fonction select est exécutée : Un événement personnalisé select est émis pour notifier les composants supérieurs avec le résultat sélectionné comme valeur.
  5. Si un clic se produit sur l'icône de fermeture, la fonction reset est exécutée : Un événement personnalisé clear est émis pour notifier le composant parent sans valeur.
  6. L'icône de recherche par défaut peut être remplacée par une icône Google Font : Fournissez l'attribut icon, accessible sur le composant avec props.icon.
  7. Le champ de recherche peut avoir différents styles, et ils doivent être fournis en tant qu'attribut HTML, par exemple pour rendre le champ arrondi : { props?.round ? " round" : ''} si props.round existe, la classe round est appliquée aux deux champs.
  8. Pour afficher les résultats de recherche en plein écran, la propriété props.max doit exister et être vraie, enfin la classe max est appliquée sur le <menu>.

Enfin, chargez et instanciez le composant c-search.riot dans une page principale nommée *index.riot:

<index-riot>
    <div style="width:600px;padding:20px;">
        <c-search placeholder="Fruit name..." value={ state.value } onchange={ changed } onkeyup={ changed } results={ state.results } onselect={ selected } onclear={ reset } outlined="true" large="true" round="true" fill="true" />
    </div>
    <script>
        import cSearch from "../components/c-search.riot"
        import fruits from "./data/fruits.js"

        const _defaultState = {
            value: "",
            results: ["Mango", "Strawberries", "Bananas"]
        }

        export default {
            components: {
                cSearch
            },
            state: _defaultState,
            changed (ev) {
                this.update({ 
                    value   : ev.target.value,
                    results : fruits.filter((el) => el.toLowerCase().includes(ev.target.value?.toLowerCase()) === true) 
                })
            },
            selected (ev) {
                this.update({ value: ev.target.value })
            },
            reset () {
                this.update(_defaultState)
            }
        }
    </script>
</index-riot>
Enter fullscreen mode Exit fullscreen mode

Source Code: https://github.com/steevepay/riot-beercss/blob/main/examples/index.search.riot

Détails du code :

  1. Le composant est importé avec import cSearch from "./components/c-search.riot"; puis chargé dans l'objet Riot components:{}.
  2. Le composant search est instancié avec <c-search /> dans le HTML.
  3. L'état de la recherche est stocké dans l'objet Riot state:{} sous la propriété state.value de type chaîne. La propriété est passée en tant qu'attribut, comme : <c-search value={ state.value } />. Dans l'état, le résultat est initialisé avec des valeurs par défaut pour agir comme un "historique de recherche". La valeur par défaut de state.results peut être initialisée à partir d'une API.
  4. Si l'entrée de recherche change, deux événements change et keyup sont déclenchés : la fonction changed est exécutée pour mettre à jour state.value, et pour rafraîchir la liste des résultats.
  5. La liste state.results prend une liste filtrée de fruits ; cependant, dans une application de production, le résultat peut être calculé à partir d'un serveur/DB et retourné par une requête HTTP API.
  6. Le résultat de la recherche est passé en tant qu'attribut du composant avec results={ state.results }.
  7. Si un clic se produit sur un élément de la liste : l'événement select est déclenché, et la fonction selected est exécutée pour définir state.value sur l'élément sélectionné. Dans une application front-end de production, vous pouvez faire un appel API, charger une page spécifique, ou exécuter toute action.
  8. Si l'icône close est cliquée, l'événement clear est émis, et la fonction reset est exécutée pour effacer l'entrée et la liste des résultats avec les valeurs par défaut.

Tests du Composant de Recherche

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

Conclusion

Voilà 🎉 Nous avons créé un composant de recherche Riot en utilisant des éléments Material Design avec BeerCSS. Le code source de la barre de recherche est disponible sur Github : https://github.com/steevepay/riot-beercss/blob/main/components/c-search.riot

Bonne journée ! Santé 🍻

Top comments (0)