FormKit inclut de nombreuses entrées prêtes à l'emploi, mais vous pouvez également définir vos propres entrées qui héritent automatiquement des fonctionnalités à valeur ajoutée de FormKit comme la validation, les messages d'erreur, la modélisation des données, le regroupement, les étiquettes, le texte d'aide et d'autres.
Si votre cas d'utilisation nécessite des modifications d'une entrée existante, telles que le déplacement de sections, le changement ou la restructuration d'éléments HTML, etc., envisagez d'utiliser la fonctionnalité d'exportation d'entrée de FormKit.
Les entrées sont composées de deux parties essentielles :
Si vous débutez avec les entrées personnalisées, envisagez de lire le guide "Créer une entrée personnalisée". Le contenu de cette page est destiné à expliquer les subtilités des entrées personnalisées pour des cas d'utilisation avancés comme la création d'un plugin ou d'une bibliothèque et n'est pas nécessaire pour de nombreux cas d'utilisation courants.
Les nouvelles entrées nécessitent une définition d'entrée. Les définitions d'entrée peuvent être enregistrées avec FormKit de trois manières :
FormKit
avec la propriété type
.Les définitions d'entrée sont des objets qui contiennent les informations nécessaires pour initialiser une entrée — comme quelles propriétés accepter, quel schéma ou composant afficher, et si des fonctions de fonctionnalités supplémentaires doivent être incluses. La structure de l'objet de définition est :
{
// Type de nœud : input, group, ou list.
type: 'input',
// Schéma à afficher (objet schéma ou fonction qui retourne un objet)
schema: [],
// Un composant Vue à afficher (utiliser schéma _OU_ composant, mais pas les deux)
component: YourComponent,
// (optionnel) Propriétés spécifiques à l'entrée que le composant <FormKit> doit accepter.
// doit être un tableau de chaînes en camelCase
props: ['fooBar'],
// (optionnel) Tableau de fonctions qui reçoivent le nœud.
features: []
}
type
Créons l'entrée la plus simple possible — une qui affiche uniquement "Hello world".
Bien que cet exemple simpliste ne contienne aucun mécanisme d'entrée/sortie, il se qualifie toujours comme une entrée complète. Elle peut avoir une valeur, exécuter des règles de validation (elles ne seront pas affichées, mais elles peuvent bloquer les soumissions de formulaires) et exécuter des plugins. Fondamentalement, toutes les entrées sont des nœuds centraux et la définition de l'entrée fournit les mécanismes pour interagir avec ce nœud.
Pour utiliser votre entrée personnalisée n'importe où dans votre application via une chaîne "type" (ex : <FormKit type="foobar" />
), vous pouvez ajouter une propriété inputs
aux options defaultConfig
. Les noms des propriétés de l'objet inputs
deviennent les chaînes "type" disponibles pour le composant <FormKit>
dans votre application.
import { createApp } from 'vue'
import App from 'App.vue'
import { plugin, defaultConfig } from '@formkit/vue'
const helloWorld = {
type: 'input',
schema: ['Hello world'],
}
createApp(App)
.use(
plugin,
defaultConfig({
inputs: {
// La propriété sera le “type” dans <FormKit type="hello">
hello: helloWorld,
},
})
)
.mount('#app')
Maintenant que nous avons défini notre entrée, nous pouvons l'utiliser n'importe où dans l'application :
L'exemple ci-dessus étend la bibliothèque @formkit/inputs
(via defaultConfig
). Cependant, une fonctionnalité puissante de FormKit est sa capacité à charger des bibliothèques d'entrées à partir de plusieurs plugins. Ces entrées peuvent ensuite être enregistrées partout où les plugins peuvent être définis :
Refactorisons notre entrée hello world pour utiliser son propre plugin :
Remarquez dans l'exemple ci-dessus que notre plugin a été défini sur un parent de l'élément qui l'a réellement utilisé ! Cela est dû à l'héritage des plugins — une fonctionnalité clé des plugins FormKit.
Votre entrée peut être écrite en utilisant le schéma de FormKit ou un composant Vue générique. Chaque approche a ses avantages et inconvénients :
Code | Avantages | Inconvénients |
---|---|---|
Vue |
|
|
Schéma |
|
|
Même si vous préférez écrire une entrée personnalisée en utilisant un composant Vue standard, vous pouvez toujours utiliser un schéma dans votre définition d'entrée. Veuillez lire la section Utilisation de createInput
pour étendre le schéma de base.
La conclusion principale est que si vous prévoyez d'utiliser une entrée personnalisée sur plusieurs projets — alors envisagez d'utiliser l'approche basée sur le schéma. Si votre entrée personnalisée ne sera utilisée que sur un seul projet et que la flexibilité n'est pas une préoccupation, utilisez un composant Vue.
À l'avenir, FormKit pourrait s'étendre pour prendre en charge des frameworks supplémentaires (ex : React ou Svelte. Si cela vous intéresse, faites-le nous savoir !.) Écrire vos entrées en utilisant un schéma signifie que vos entrées seront compatibles (peut-être avec des changements minimes) avec ces frameworks également.
Toutes les entrées de base de FormKit sont écrites en utilisant des schémas pour permettre la plus grande flexibilité possible. Vous avez deux options principales lorsque vous écrivez vos propres entrées de schéma :
Il est important de comprendre la structure de base d'une entrée "standard" de FormKit, qui est divisée en sections :
La section input
dans le diagramme ci-dessus est généralement ce que vous allez remplacer lors de la création de vos propres entrées — en conservant les enveloppes, les étiquettes, le texte d'aide et les messages intacts. Cependant, si vous souhaitez également contrôler ces aspects, vous pouvez également écrire votre propre entrée à partir de zéro.
createInput
pour étendre le schéma de basePour créer des entrées en utilisant le schéma de base, vous pouvez utiliser l'utilitaire createInput()
du package @formkit/vue
. Cette fonction accepte 3 arguments :
input
(voir le diagramme ci-dessus).La fonction retourne une définition d'entrée prête à l'emploi.
Lorsque vous fournissez un composant comme premier argument, createInput
générera un objet de schéma qui fait référence à votre composant dans le schéma de base. Votre composant recevra une seule prop context
:
{
$cmp: 'YourComponent',
props: {
context: '$node.context'
}
}
Lorsque vous fournissez un objet de schéma, votre schéma est directement injecté dans l'objet de schéma de base. Remarquez que notre exemple de bonjour prend maintenant en charge la sortie de fonctionnalités "standard" de FormKit comme les étiquettes, le texte d'aide et la validation :
Il peut être judicieux d'écrire vos entrées complètement à partir de zéro sans utiliser aucune des fonctionnalités de base du schéma. Dans ce cas, fournissez simplement la définition de l'entrée de votre objet de schéma complet.
Dans l'exemple ci-dessus, nous avons pu recréer les mêmes fonctionnalités que l'exemple createInput
— à savoir — l'étiquette, le texte d'aide et la sortie du message de validation. Cependant, il nous manque encore un certain nombre de fonctionnalités "standard" de FormKit, comme le support des slots. Si vous essayez de publier votre entrée ou de maintenir la compatibilité de l'API avec les autres entrées de FormKit, jetez un œil à la liste de vérification des entrées.
Lors de la rédaction d'une entrée personnalisée FormKit en utilisant des composants Vue, il est recommandé de ne pas utiliser les composants FormKit à l'intérieur, les entrées personnalisées sont censées être écrites comme des entrées régulières avec l'avantage d'utiliser la prop de contexte FormKit pour ajouter la fonctionnalité requise par FormKit, si votre cas est d'utiliser un composant FormKit avec des valeurs par défaut, il est recommandé à la place d'utiliser une enveloppe de composant Vue et d'appeler directement ce composant, les entrées FormKit fonctionnent à tout niveau d'imbrication, ou vous pouvez également envisager d'utiliser la fonctionnalité d'exportation d'entrée de FormKit pour ajouter des fonctionnalités et changer les attrs et props.
Pour la plupart des utilisateurs, passer un composant Vue à createInput
offre un bon équilibre entre personnalisation et fonctionnalités à valeur ajoutée. Si vous souhaitez vous éloigner complètement des entrées basées sur des schémas, vous pouvez passer un composant directement à une définition d'entrée.
Les entrées de composants reçoivent une seule prop — l'objet context
. Il vous appartient ensuite d'écrire un composant qui englobe les fonctionnalités souhaitées de FormKit (étiquettes, texte d'aide, affichage des messages, etc.). Consultez la liste de vérification des entrées pour une liste de ce que vous voudrez produire.
Les entrées ont deux rôles critiques :
Vous pouvez recevoir une entrée de n'importe quelle interaction utilisateur et l'entrée peut définir sa valeur à n'importe quel type de données. Les entrées ne sont pas limitées aux chaînes et aux nombres — elles peuvent stocker avec plaisir des tableaux, des objets ou des structures de données personnalisées.
Fondamentalement, tout ce qu'une entrée doit faire est d'appeler node.input(value)
avec une valeur. La méthode node.input()
est automatiquement temporisée, alors n'hésitez pas à l'appeler fréquemment — comme à chaque frappe. Typiquement, cela ressemble à une liaison à l'événement input
.
L'objet context
comprend un gestionnaire d'entrée pour les types d'entrée de base : context.handlers.DOMInput
. Cela peut être utilisé pour des entrées de type texte où la valeur de l'entrée est disponible à event.target.value
. Si vous avez besoin d'un gestionnaire d'événements plus complexe, vous pouvez l'exposer en utilisant "features".
Toute interaction utilisateur peut être considérée comme un événement d'entrée. Pour de nombreuses entrées HTML natives, cette interaction est capturée avec l'événement input.
// Une entrée de texte HTML écrite dans le schéma :
{
$el: 'input',
attrs: {
onInput: '$handlers.DOMInput'
}
}
L'équivalent dans un template Vue :
<template>
<input @input="context.DOMInput" />
</template>
Les entrées sont également responsables de l'affichage de la valeur actuelle. Typiquement, vous voudrez utiliser node._value
ou $_value
dans le schéma pour afficher une valeur. C'est la valeur "en direct" non temporisée. La valeur actuellement validée est node.value
($value
). Lisez plus sur "l'établissement de la valeur" ici.
// Un champ de saisie de texte HTML écrit en schéma :
{
$el: 'input',
attrs: {
onInput: '$handlers.DOMInput',
value: '$_value'
}
}
L'équivalent dans un template Vue :
<template>
<input :value="context._value" @input="context.handlers.DOMInput" />
</template>
La seule fois où l'entrée non validée _value
devrait être utilisée est pour afficher la valeur sur l'entrée elle-même — dans tous les autres emplacements, il est important d'utiliser la valeur validée value
.
Les props standards de FormKit que vous pouvez passer au composant <FormKit>
(comme label
ou type
) sont disponibles à la racine de l'objet contexte et dans les props du nœud central, et vous pouvez utiliser ces props dans votre schéma en les référençant directement dans les expressions (ex : $label
). Toutes les props passées à un composant <FormKit>
qui ne sont pas des props de nœud se retrouvent dans l'objet context.attrs
(juste $attrs
dans le schéma).
Si vous avez besoin de props supplémentaires, vous pouvez les déclarer dans votre définition d'entrée. Les props peuvent également être utilisées pour accepter de nouvelles props du composant <FormKit>
, mais elles sont également utilisées pour l'état interne de l'entrée (un peu comme une ref
dans un composant Vue 3).
FormKit utilise l'espace de noms props
pour les deux usages (voir l'exemple d'autocomplétion ci-dessous pour un exemple de cela). Les props doivent toujours être définies en camelCase et utilisées dans vos templates Vue en kebab-case. Il y a 2 façons de définir des props :
Lors de l'extension du schéma de base en utilisant l'assistant createInput
, passez un second argument avec les valeurs de définition d'entrée à fusionner :
La notation par objet vous donne un contrôle précis sur la façon dont vos props sont définies en vous permettant de :
boolean
qui peuvent être passées sans valeur.node.addProps()
)Vous pouvez ajouter dynamiquement des props en utilisant la méthode node.addProps()
dans tout environnement d'exécution où vous avez accès au nœud. Pour les entrées personnalisées, cela est particulièrement utile lorsqu'il est utilisé dans des fonctionnalités. La notation par tableau et la notation par objet sont prises en charge (voir ci-dessus).
Les fonctionnalités sont le moyen privilégié d'ajouter des fonctionnalités à un type d'entrée personnalisé. Une "fonctionnalité" est simplement une fonction qui reçoit le nœud de base en argument. Effectivement, ce sont des plugins sans héritage (ils s'appliquent donc uniquement au nœud actuel). Vous pouvez utiliser les fonctionnalités pour ajouter des gestionnaires d'entrée, manipuler des valeurs, interagir avec des props, écouter des événements, et bien plus encore.
Les fonctionnalités sont définies dans un tableau pour encourager la réutilisation du code lorsque c'est possible. Par exemple, nous utilisons une fonctionnalité appelée “options” sur les entrées select
, checkbox
et radio
.
Comme exemple, imaginons que vous voulez construire une entrée qui permet aux utilisateurs de saisir deux nombres, et que la valeur de l'entrée est la somme de ces deux nombres :
FormKit est écrit en TypeScript et inclut des définitions de type pour toutes ses entrées de base. Si vous écrivez vos propres entrées et souhaitez fournir un support TypeScript, vous pouvez définir vos propres entrées en utilisant deux augmentations de module :
La prop type
du composant <FormKit>
est une chaîne de caractères qui est utilisée comme la clé d'une union discriminée de props (FormKitInputProps
). En augmentant ce type, vos entrées personnalisées peuvent définir leurs propres types de props. Pour ce faire, vous devez augmenter le type FormKitInputProps
pour ajouter vos propres types personnalisés :
declare module '@formkit/inputs' {
interface FormKitInputProps<Props extends FormKitInputs<Props>> {
// Cette clé et le `type` doivent correspondre :
'my-input': {
// Définissez votre `type` d'entrée :
type: 'my-input',
// Définissez une prop optionnelle. Utilisez camelCase pour tous les noms de props :
myOptionalProp?: string | number
// Définissez une prop obligatoire
superImportantProp: number
// Définissez le type de valeur, cela devrait toujours être optionnel !
value?: string | number
// Utilisez le générique Prop pour inférer des informations d'un autre champ, remarquez
// que nous utilisons un utilitaire "PropType" pour inférer le type de la `value` à partir du générique Props :
someOtherProp?: PropType<Props, 'value'>
}
}
}
Si vous définissez vos propres sections (slots) dans votre entrée personnalisée, vous pouvez également ajouter le support TypeScript pour ceux-ci. Pour ce faire, vous devez augmenter le type FormKitInputSlots
pour ajouter vos propres slots personnalisés :
declare module '@formkit/inputs' {
interface FormKitInputProps<Props extends FormKitInputs<Props>> {
'my-input' {
type: 'my-input'
// ... propriétés ici
}
}
interface FormKitInputSlots<Props extends FormKitInputs<Props>> {
'my-input': FormKitBaseSlots<Props>
}
}
Dans l'exemple ci-dessus, nous utilisons FormKitBaseSlots
— un utilitaire TypeScript pour ajouter tous les slots "de base" que la plupart des entrées personnalisées implémentent, comme outer
, label
, help
, message
, etc. Cependant, vous pourriez également définir entièrement vos propres slots à partir de zéro, ou augmenter FormKitBaseSlots
pour ajouter des slots supplémentaires (FormKitBaseSlots<Props> & YourCustomSlots
).
declare module '@formkit/inputs' {
// ... propriétés ici
interface FormKitInputSlots<Props extends FormKitInputs<Props>> {
'my-input': {
// Ceci sera le *seul* slot disponible sur l'entrée my-input
slotName: FormKitFrameworkContext & {
// ceci sera disponible comme données de slot dans le slot `slotName`
fooBar: string
}
}
}
}
}
Pour augmenter les FormKitInputSlots
, vous devez d'abord avoir écrit une augmentation pour FormKitInputProps
qui inclut au moins la propriété type
.
Ci-dessous se trouvent quelques exemples d'entrées personnalisées. Ils ne sont pas destinés à être exhaustifs ou prêts pour la production, mais illustrent plutôt certaines fonctionnalités d'entrée personnalisée.
Ceci est l'entrée la plus simple possible et n'utilise aucune des structures DOM intégrées de FormKit et ne produit qu'une entrée de texte — cependant, c'est un membre entièrement fonctionnel du groupe dans lequel elle est imbriquée et capable de lire et d'écrire des valeurs.
Dans l'exemple ci-dessus, $handlers.DOMInput
est une fonction de commodité intégrée pour (event) => node.input(event.target.value)
.
Examinons un exemple légèrement plus complexe qui utilise createInput
pour fournir toute la structure standard de FormKit tout en offrant une interface d'entrée personnalisée.
FormKit expose des dizaines de fonctionnalités à valeur ajoutée, même pour les entrées les plus banales. Lors de la création d'une entrée personnalisée pour un projet spécifique, vous n'avez besoin de mettre en œuvre que les fonctionnalités qui seront réellement utilisées sur ce projet. Cependant, si vous prévoyez de distribuer vos entrées à d'autres, vous voudrez vous assurer que ces fonctionnalités sont disponibles. Par exemple, l'entrée standard <FormKit type="text">
utilise le schéma suivant pour son élément input
:
{
$el: 'input',
bind: '$attrs',
attrs: {
type: '$type',
disabled: '$disabled',
class: '$classes.input',
name: '$node.name',
onInput: '$handlers.DOMInput',
onBlur: '$handlers.blur',
value: '$_value',
id: '$id',
}
}
Il y a plusieurs fonctionnalités dans le schéma ci-dessus qui peuvent ne pas être immédiatement évidentes, comme le gestionnaire onBlur
. La liste de vérification suivante est destinée à aider les auteurs d'entrées à couvrir toutes leurs bases :
label
prop must be displayed and linked for accessibility with the for
attribute.label
slot.label
section key.help
prop must be displayed.help
slot.help
section key.context.messages
object must displayed if it is set to visible
.messages
slot.messages
section key.message
slot.message
section key..input
slot.id
attribute (context.id
).name
attribute (context.node.name
).context.handlers.blur
when blurred.node.input(value)
when the user provides input. You can use context.handlers.DOMInput
for text-like inputs.context._value
.disabled
attribute when context.disabled
is true
.bind: '$attrs'
in schemas.