Créer une entrée personnalisée

Dans ce guide, nous allons vous guider à travers le processus de création, d'enregistrement et d'utilisation d'une entrée personnalisée. Plus précisément, nous allons créer une entrée "mot de passe à usage unique" ("OTP" pour faire court). Les OTP sont couramment utilisés pour l'authentification à deux facteurs lorsqu'un utilisateur doit taper un code envoyé par SMS ou par une application d'authentification. Commençons !

Outil de construction SFC

Ce guide suppose que vous utilisez un outil de construction Vue 3 standard comme Vite, Nuxt 3, ou Vue CLI qui vous permettra d'importer des composants à fichier unique .vue.

Création d'un composant

Pour commencer, créons le fichier de composant de notre entrée. Nous l'appellerons OneTimePassword.vue :

<script setup>
  const props = defineProps({
    context: Object,
  })
</script>

<template>
  <div>Plus à venir ici...</div>
</template>

FormKit fournit beaucoup de fonctionnalités d'entrée prêtes à l'emploi que nous allons vouloir conserver - comme les étiquettes, le texte d'aide, et l'affichage des messages d'erreur. Tout ce que nous voulons vraiment modifier est la section d'entrée de notre entrée. Nous pouvons conserver ces fonctionnalités standard de FormKit en utilisant la fonction utilitaire createInput du package @formkit/vue.

Au fur et à mesure que nous construisons notre entrée, nous voudrons visualiser son progrès, alors créons un formulaire d'échantillon pour :

  1. Importer OneTimePassword.vue
  2. Passer ce composant importé à createInput()
  3. Utiliser la valeur de retour (une définition d'entrée) comme la propriété type d'un composant <FormKit>.

Nous appellerons ce formulaire d'échantillon Register.vue :

Charger l'exemple en direct

Excellent ! Maintenant, nous pouvons itérer sur notre fichier OneTimePassword.vue et voir les résultats. L'une des premières choses à remarquer est comment notre entrée prend déjà en charge les étiquettes, le texte d'aide, la validation, et d'autres propriétés universelles de FormKit. Ces fonctionnalités sont fournies par createInput().

Aussi, avez-vous remarqué cette balise <pre> dans l'exemple ci-dessus ? Elle affiche l'état actuel des données du formulaire. Nous l'utiliserons pour visualiser la valeur de notre entrée personnalisée. Comme notre entrée n'a actuellement aucune valeur, elle n'apparaît pas dans les données du formulaire. Il est temps de changer cela !

Entrée & sortie

Ouvrons à nouveau OneTimePassword.vue et changeons notre <div> en une balise <input>. Nous commencerons par une seule entrée de texte, et nous progresserons à partir de là. Mais comment définir et afficher réellement la valeur de notre entrée personnalisée ?

Toutes les entrées personnalisées reçoivent l'omnipotent objet contexte comme propriété context. Pour que notre entrée puisse définir sa valeur, elle doit appeler context.node.input(value). Pour afficher correctement la valeur de notre entrée, nous devrions définir l'attribut :value de l'entrée à context._value.

Charger l'exemple en direct

Notre petite entrée a bien grandi ! Elle n'est peut-être pas jolie, mais elle lit et écrit maintenant des valeurs. Pour preuve, essayez de définir la valeur initiale de l'objet values du formulaire à { two_factor_code: '12345' } et vous verrez que l'entrée est automatiquement peuplée avec la valeur.

Exigences pour notre entrée

Ok, maintenant que nous comprenons comment créer une entrée, comment l'utiliser, et comment lire et écrire des valeurs — attaquons-nous à la véritable "logique métier" de notre entrée de mot de passe à usage unique. Voici nos exigences :

  • Les utilisateurs entrent une série de chiffres, et chaque chiffre a sa propre balise <input>.
  • La valeur de l'entrée doit toujours être tous les chiffres concaténés.
  • Nous ne voulons que l'entrée change sa valeur que si tous les chiffres ont été complétés (pas besoin de valider à chaque frappe si l'utilisateur n'a pas terminé).
  • Il devrait permettre à un utilisateur de cliquer sur un chiffre donné pour le modifier.
  • Lorsqu'un utilisateur tape un nombre, il devrait automatiquement se concentrer sur l'entrée suivante.
  • Il devrait supporter le copier/coller.

Ajout d'une prop

Pour notre première exigence, nous avons besoin de n balises <input>. Il serait peut-être préférable d'exposer le nombre de chiffres en tant que prop. Pour ce faire, nous devons informer notre fonction createInput que nous voulons accepter une nouvelle prop :

createInput(OneTimePassword, {
  props: ['digits'],
})

Nous avons maintenant accès à context.digits. Retour dans OneTimePassword.vue, utilisons cela pour afficher le bon nombre de balises <input>.

Charger l'exemple en direct

OK — nous avons plusieurs entrées ! Notre première exigence est complète :

  • Les utilisateurs entrent une série de chiffres, et chaque chiffre a sa propre balise <input>.
Styling

Nous avons ajouté une touche de CSS dans l'exemple ci-dessus, mais en général, nous n'allons pas nous plonger dans le style dans ce guide. Il est recommandé d'utiliser context.classes.yourKey comme nom de classe des éléments de votre entrée.

Interactivité

Remarquez dans l'exemple ci-dessus que lorsque vous tapez dans une entrée, toutes les autres entrées sont synchronisées à la même valeur ? Plutôt sympa, mais ce n'est pas ce que nous voulons. C'est parce que nous utilisons toujours le même gestionnaire d'entrée et :value. Voici un plan pour améliorer notre entrée :

  • Chaque entrée ne doit modifier que le caractère à son index respectif dans la chaîne finale.
  • Le gestionnaire d'entrée doit appeler focus() sur l'entrée suivante.
  • Lorsque la chaîne a la même longueur que digits, nous mettons à jour la valeur de l'entrée en appelant context.node.input().
Charger l'exemple en direct

Super ! Cela commence à fonctionner comme nous l'attendons. Vérifions à nouveau nos exigences :

  • Les utilisateurs entrent une série de chiffres, et chaque chiffre a sa propre balise <input>.
  • La valeur de l'entrée doit toujours être tous les chiffres concaténés.
  • Nous ne voulons que l'entrée change sa valeur que si tous les chiffres ont été complétés (pas besoin de valider à chaque frappe si l'utilisateur n'a pas terminé).
  • Il devrait permettre à un utilisateur de cliquer sur un chiffre donné pour le modifier.
  • Lorsqu'un utilisateur tape un nombre, il devrait automatiquement se concentrer sur l'entrée suivante.
  • Il devrait supporter le copier/coller.

Copier & coller

Il semble que nous n'ayons plus qu'une chose à faire : le support de copier & coller. Heureusement, les navigateurs ont un événement paste. Pour garantir une expérience utilisateur de premier ordre, nous ferons une supposition : si un utilisateur copie/colle, il essaie de copier et coller l'ensemble du code. Pas un seul chiffre du code. Cela semble raisonnable.

Tout ce que nous avons à faire est de capturer l'événement de copier/coller sur l'une de nos balises d'entrée, d'obtenir le texte collé, et de définir la valeur tmp à cette chaîne de chiffres. Préparons un autre gestionnaire d'événements :

handlePaste(e) {
  const paste = e.clipboardData.getData('text')
  if (typeof paste === 'string') {
    // Si c'est de la bonne longueur, collez-le.
    this.tmp = paste.substr(0, this.max)
    const inputs = e.target.parentElement.querySelectorAll('input')
    // Concentrez-vous sur le dernier caractère
    inputs.item(this.tmp.length - 1).focus()
  }
}
Charger l'exemple en direct

Toutes nos exigences sont maintenant remplies !

Inscription

Maintenant que nous avons élaboré une excellente entrée, enregistrons-la avec notre application afin de pouvoir l'utiliser n'importe où en utilisant simplement la chaîne otp. Ouvrez le fichier principal de votre application Vue (où app.use(formKit) est). Nous allons simplement l'ajouter :

import { createApp } from 'Vue'
import App from 'App.vue'
import OneTimePassword from './OneTimePassword.vue'
import { plugin, defaultConfig, createInput } from '@formkit/vue'

const app = createApp(App)
app.use(
  plugin,
  defaultConfig({
    inputs: {
      otp: createInput(OneTimePassword, {
        props: ['digits'],
      }),
    },
  })
)
app.mount('#app')

C'est fait ! Maintenant, vous pouvez utiliser votre entrée n'importe où dans votre application :

<FormKit type="otp" digits="4" />

Prochaines étapes

Notre entrée de mot de passe à usage unique fonctionne très bien ! Voici quelques idées de fonctionnalités supplémentaires que nous pourrions développer encore plus :

  • Une règle de validation accompagnante pour effectuer un appel d'authentification à deux facteurs vers le backend.
  • Des styles supplémentaires pour vraiment le faire ressortir.
  • Si le formulaire ne contient qu'une entrée de mot de passe à usage unique, vous pourriez soumettre automatiquement le formulaire !
  • Complétez la liste de contrôle des entrées personnalisées.
  • Publiez-le ! Si cette entrée (ou toute autre que vous créez) vous est utile, elle est probablement utile à d'autres personnes aussi. Vous pourriez envisager de l'ouvrir à la source !

Espérons que ce guide vous a aidé à comprendre comment les entrées personnalisées sont déclarées, écrites et enregistrées. Si vous voulez approfondir, essayez de lire sur les internes de base de FormKit et la création d'entrées personnalisées !

Vous en voulez plus ? Commencez par lire sur le noyau de FormKit.Creusez plus profondément