O FormKit inclui várias entradas prontas para uso, mas você também pode definir suas próprias entradas que herdam automaticamente os recursos de valor agregado do FormKit, como validação, mensagens de erro, modelagem de dados, agrupamento, rótulos, texto de ajuda e outros.
Se o seu caso de uso requer modificações em uma entrada existente, como mover seções, alterar ou reestruturar elementos HTML, etc., considere usar o recurso de exportação de entrada do FormKit.
As entradas são compostas por duas partes essenciais:
Se você está apenas começando com entradas personalizadas, considere ler o guia “Criar uma entrada personalizada”. O conteúdo desta página é destinado a explicar as complexidades das entradas personalizadas para casos de uso avançados, como a criação de um plugin ou biblioteca, e não é necessário para muitos casos de uso comuns.
Novas entradas requerem uma definição de entrada. As definições de entrada podem ser registradas com o FormKit de três maneiras:
FormKit
com a propriedade type
.As definições de entrada são objetos que contêm as informações necessárias para inicializar uma entrada — como quais propriedades aceitar, qual esquema ou componente renderizar e se quaisquer funções de recursos adicionais devem ser incluídas. A estrutura do objeto de definição é:
{
// Tipo de nó: input, group ou list.
type: 'input',
// Esquema para renderizar (objeto de esquema ou função que retorna um objeto)
schema: [],
// Um componente Vue para renderizar (use esquema _OU_ componente, mas não ambos)
component: YourComponent,
// (opcional) Propriedades específicas da entrada que o componente <FormKit> deve aceitar.
// deve ser um array de strings em camelCase
props: ['fooBar'],
// (opcional) Array de funções que recebem o nó.
features: []
}
type
Vamos fazer o input mais simples possível — um que apenas exibe "Hello world".
Embora este exemplo simplista não contenha nenhum mecanismo de entrada/saída, ele ainda se qualifica como um input completo. Ele pode ter um valor, executar regras de validação (elas não serão exibidas, mas podem bloquear envios de formulários) e executar plugins. Fundamentalmente, todos os inputs são nós centrais e a definição do input fornece os mecanismos para interagir com esse nó.
Para usar seu input personalizado em qualquer lugar da sua aplicação através de uma string "type" (ex: <FormKit type="foobar" />
), você pode adicionar uma propriedade inputs
às opções de defaultConfig
. Os nomes das propriedades do objeto inputs
se tornam as strings "type" disponíveis para o componente <FormKit>
na sua aplicação.
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: {
// A propriedade será o “type” em <FormKit type="hello">
hello: helloWorld,
},
})
)
.mount('#app')
Agora que definimos nosso input, podemos usá-lo em qualquer lugar da aplicação:
O exemplo acima estende a biblioteca @formkit/inputs
(via defaultConfig
). No entanto, um recurso poderoso do FormKit é sua capacidade de carregar bibliotecas de inputs de múltiplos plugins. Esses inputs podem então ser registrados em qualquer lugar onde plugins podem ser definidos:
Vamos refatorar nosso input hello world para usar seu próprio plugin:
Observe no exemplo acima que nosso plugin foi definido em um elemento pai daquele que realmente o usou! Isso é graças à herança de plugins — um recurso central dos plugins do FormKit.
Seu input pode ser escrito usando o esquema do FormKit ou um componente Vue genérico. Existem prós e contras para cada abordagem:
Código | Prós | Contras |
---|---|---|
Vue |
|
|
Esquema |
|
|
Mesmo que você prefira escrever um input personalizado usando um Componente Vue padrão, você ainda pode usar um esquema na definição do seu input. Por favor, leia a seção Usando createInput
para estender o esquema base.
A principal conclusão é que se você planeja usar um input personalizado em vários projetos — então considere usar a abordagem baseada em esquema. Se o seu input personalizado será usado apenas em um único projeto e a flexibilidade não é uma preocupação, use um componente Vue.
No futuro, o FormKit pode se expandir para suportar frameworks adicionais (ex: React ou Svelte. Se isso é algo que lhe interessa, nos avise!.) Escrever seus inputs usando esquemas significa que seus inputs serão compatíveis (talvez com mudanças mínimas) com esses frameworks também.
Todos os inputs principais do FormKit são escritos usando esquemas para permitir a maior flexibilidade possível. Você tem duas opções principais ao escrever seus próprios inputs de esquema:
É importante entender a estrutura básica de um input "padrão" do FormKit, que é dividido em seções:
A seção input
no diagrama acima é tipicamente o que você vai substituir ao criar seus próprios inputs — mantendo os invólucros, rótulos, texto de ajuda e mensagens intactos. No entanto, se você também quiser controlar esses aspectos, pode escrever seu próprio input do zero.
createInput
para estender o esquema basePara criar inputs usando o esquema base, você pode usar a utilidade createInput()
do pacote @formkit/vue
. Esta função aceita 3 argumentos:
input
(veja o diagrama acima).A função retorna uma definição de input pronta para uso.
Ao fornecer um componente como o primeiro argumento, createInput
irá gerar um objeto de esquema que referencia seu componente dentro do esquema base. Seu componente receberá uma única propriedade context
:
{
$cmp: 'YourComponent',
props: {
context: '$node.context'
}
}
Ao fornecer um objeto de esquema, seu esquema é injetado diretamente no objeto de esquema base. Observe que nosso exemplo de "olá mundo" agora suporta a saída de recursos "padrão" do FormKit, como rótulos, texto de ajuda e validação:
Pode fazer sentido escrever suas entradas completamente do zero sem usar nenhum dos recursos do esquema base. Ao fazer isso, basta fornecer a definição de entrada com o seu objeto de esquema completo.
No exemplo acima, conseguimos recriar os mesmos recursos do exemplo createInput
— ou seja — rótulo, texto de ajuda e saída de mensagem de validação. No entanto, ainda estamos perdendo uma série de recursos "padrão" do FormKit, como suporte a slots. Se você está tentando publicar sua entrada ou manter a compatibilidade da API com as outras entradas do FormKit, dê uma olhada na lista de verificação de entrada.
Ao escrever uma entrada personalizada do FormKit enquanto usa componentes Vue, é recomendado não usar os componentes do FormKit internamente, as entradas personalizadas devem ser escritas como entradas regulares com a vantagem de usar a propriedade de contexto do FormKit para adicionar a funcionalidade que o FormKit exige. Se o seu caso é usar um componente do FormKit com valores padrão, é recomendado, em vez disso, usar um wrapper de componente Vue e chamar diretamente esse componente. As entradas do FormKit funcionam em qualquer nível de aninhamento, ou você também pode considerar usar o recurso de exportação de entrada do FormKit para adicionar recursos e alterar atributos e props.
Para a maioria dos usuários, passar um componente Vue para createInput
oferece um bom equilíbrio entre personalização e recursos de valor agregado. Se você deseja se desvincular completamente das entradas baseadas em esquema, pode passar um componente diretamente para uma definição de entrada.
As entradas de componentes recebem uma única propriedade — o objeto context
. Depois, cabe a você escrever um componente que englobe os recursos desejados do FormKit (rótulos, texto de ajuda, exibição de mensagens, etc.). Confira a lista de verificação de entrada para uma lista do que você vai querer produzir.
As entradas têm dois papéis críticos:
Você pode receber entrada de qualquer interação do usuário e a entrada pode definir seu valor para qualquer tipo de dado. As entradas não são limitadas a strings e números — elas podem armazenar Arrays, Objetos ou estruturas de dados personalizadas.
Fundamentalmente, tudo o que uma entrada precisa fazer é chamar node.input(value)
com um valor. O método node.input()
é automaticamente debounced, então sinta-se à vontade para chamá-lo com frequência — como a cada tecla pressionada. Tipicamente, isso se parece com a vinculação ao evento input
.
O objeto context
inclui um manipulador de entrada para tipos de entrada básicos: context.handlers.DOMInput
. Isso pode ser usado para entradas semelhantes a texto onde o valor da entrada está disponível em event.target.value
. Se você precisar de um manipulador de eventos mais complexo, você pode expor isso usando "features".
Qualquer interação do usuário pode ser considerada um evento de entrada. Para muitas entradas HTML nativas, essa interação é capturada com o evento de entrada.
// Uma entrada de texto HTML escrita em esquema:
{
$el: 'input',
attrs: {
onInput: '$handlers.DOMInput'
}
}
O equivalente em um template Vue:
<template>
<input @input="context.DOMInput" />
</template>
Entradas também são responsáveis por exibir o valor atual. Tipicamente, você vai querer usar o node._value
ou $_value
no esquema para exibir um valor. Este é o valor "ao vivo" não debounced. O valor atualmente confirmado é node.value
($value
). Leia mais sobre "assentamento de valor" aqui.
// Um input de texto HTML escrito em esquema:
{
$el: 'input',
attrs: {
onInput: '$handlers.DOMInput',
value: '$_value'
}
}
O equivalente em um template Vue:
<template>
<input :value="context._value" @input="context.handlers.DOMInput" />
</template>
A única vez que o input não confirmado _value
deve ser usado é para exibir o valor no próprio input — em todos os outros locais, é importante usar o valor confirmado value
.
As props padrão do FormKit que você pode passar para o componente <FormKit>
(como label
ou type
) estão disponíveis na raiz do objeto de contexto e nas props do núcleo do nó, e você pode usar essas props no seu esquema referenciando-as diretamente em expressões (ex: $label
). Qualquer prop passada para um componente <FormKit>
que não seja props do nó acaba no objeto context.attrs
(apenas $attrs
no esquema).
Se você precisar de props adicionais, você pode declará-las na definição da sua entrada. Props também podem ser usadas para aceitar novas props do componente <FormKit>
, mas também são usadas para o estado interno da entrada (muito como um ref
em um componente Vue 3).
FormKit usa o namespace props
para ambos os propósitos (veja o exemplo de autocomplete abaixo para um exemplo disso). Props devem sempre ser definidas em camelCase e usadas nos seus templates Vue com kebab-case. Existem 2 maneiras de definir props:
Ao estender o esquema base usando o auxiliar createInput
, passe um segundo argumento com valores de definição de entrada para mesclar:
A notação de objeto te dá controle detalhado sobre como suas props são definidas, permitindo que você:
boolean
que podem ser passadas sem um valor.node.addProps()
)Você pode adicionar props dinamicamente usando o método node.addProps()
em qualquer ambiente de execução onde você tenha acesso ao nó. Para entradas personalizadas, isso é particularmente útil quando usado em funcionalidades. Tanto a notação de array quanto a notação de objeto são suportadas (veja acima).
Funcionalidades são a maneira preferida de adicionar funcionalidades a um tipo de entrada personalizado. Uma "funcionalidade" é simplesmente uma função que recebe o nó central como argumento. Efetivamente, elas são plugins sem herança (então elas só se aplicam ao nó atual). Você pode usar funcionalidades para adicionar manipuladores de entrada, manipular valores, interagir com props, ouvir eventos e muito mais.
Funcionalidades são definidas em um array para incentivar a reutilização de código quando possível. Por exemplo, nós usamos uma funcionalidade chamada “opções” em entradas do tipo select
, checkbox
e radio
.
Como exemplo, vamos imaginar que você queira construir uma entrada que permite aos usuários inserir dois números, e o valor da entrada é a soma desses dois números:
O FormKit é escrito em TypeScript e inclui definições de tipo para todas as suas entradas centrais. Se você está escrevendo suas próprias entradas e gostaria de fornecer suporte ao TypeScript, você pode definir suas próprias entradas usando duas ampliações de módulo:
A prop type
do componente <FormKit>
é uma string que é usada como a chave de uma união discriminada de props (FormKitInputProps
). Ao ampliar esse tipo, suas entradas personalizadas podem definir seus próprios tipos de prop. Para fazer isso, você deve ampliar o tipo FormKitInputProps
para adicionar seus próprios tipos personalizados:
declare module '@formkit/inputs' {
interface FormKitInputProps<Props extends FormKitInputs<Props>> {
// Esta chave e o `type` devem coincidir:
'my-input': {
// Defina o `type` da sua entrada:
type: 'my-input',
// Defina uma prop opcional. Use camelCase para todos os nomes de prop:
myOptionalProp?: string | number
// Defina uma prop obrigatória
superImportantProp: number
// Defina o tipo de valor, isso sempre deve ser opcional!
value?: string | number
// Use o genérico Prop para inferir informações de outro campo, observe
// que usamos uma utilidade "PropType" para inferir o tipo do `value` do genérico Props:
someOtherProp?: PropType<Props, 'value'>
}
}
}
Se você definir suas próprias seções (slots) na sua entrada personalizada, você também pode adicionar suporte TypeScript para elas. Para fazer isso, você deve aumentar o tipo FormKitInputSlots
para adicionar seus próprios slots personalizados:
declare module '@formkit/inputs' {
interface FormKitInputProps<Props extends FormKitInputs<Props>> {
'my-input' {
type: 'my-input'
// ... propriedades aqui
}
}
interface FormKitInputSlots<Props extends FormKitInputs<Props>> {
'my-input': FormKitBaseSlots<Props>
}
}
No exemplo acima, usamos FormKitBaseSlots
— uma utilidade TypeScript para adicionar todos os "slots básicos" que a maioria das entradas personalizadas implementam, como outer
, label
, help
, message
, etc. No entanto, você também poderia definir seus próprios slots completamente do zero, ou aumentar FormKitBaseSlots
para adicionar slots adicionais (FormKitBaseSlots<Props> & YourCustomSlots
).
declare module '@formkit/inputs' {
// ... propriedades aqui
interface FormKitInputSlots<Props extends FormKitInputs<Props>> {
'my-input': {
// Este será o *único* slot disponível na entrada my-input
slotName: FormKitFrameworkContext & {
// isso estará disponível como dados de slot no slot `slotName`
fooBar: string
}
}
}
}
}
Para aumentar o FormKitInputSlots
, você deve primeiro ter escrito um aumento para FormKitInputProps
que pelo menos inclua a propriedade type
.
Abaixo estão alguns exemplos de entradas personalizadas. Eles não são destinados a ser abrangentes ou prontos para produção, mas sim ilustrar algumas características de entrada personalizada.
Esta é a entrada mais simples possível e não aproveita nenhuma das estruturas DOM incorporadas do FormKit e apenas produz uma entrada de texto — no entanto, é um membro totalmente funcional do grupo em que está aninhado e capaz de ler e escrever valores.
No exemplo acima, o $handlers.DOMInput
é uma função de conveniência integrada para (event) => node.input(event.target.value)
.
Vamos dar uma olhada em um exemplo um pouco mais complexo que utiliza createInput
para fornecer toda a estrutura padrão do FormKit, enquanto ainda fornece uma interface de entrada personalizada.
FormKit expõe dezenas de recursos valiosos até mesmo para as entradas mais comuns. Ao escrever uma entrada personalizada para um projeto específico, você só precisa implementar os recursos que serão realmente utilizados naquele projeto. No entanto, se você planeja distribuir suas entradas para outras pessoas, você vai querer garantir que esses recursos estejam disponíveis. Por exemplo, a entrada padrão <FormKit type="text">
usa o seguinte esquema para o seu elemento 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',
}
}
Há vários recursos no esquema acima que podem não ser imediatamente óbvios, como o manipulador onBlur
. A seguinte lista de verificação é destinada a ajudar os autores de entrada a cobrir todas as suas 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.