-
Notifications
You must be signed in to change notification settings - Fork 1
Forms pt BR
🌐 Esta página em: English · Português
Um formulário no eQuantic.UI é um modelo, não um widget. Os valores, as flags, as regras, os
erros e o único submit que pode estar em voo vivem todos em eQuantic.UI.Primitives — lógica pura,
zero dependências, sem pixels — e os componentes por cima só sabem qual dos dois é dono de quê.
Por que uma camada de modelo? Porque um formulário é quase todo aritmética sobre estado: se um erro já pode ser mostrado, se há algo por salvar, se um segundo clique pode submeter de novo. Costure isso num widget e só dá para testar clicando. Mantenha aqui e testa-se afirmando — e o mesmo C# roda no servidor, no browser pelo gêmeo transpilado, e numa janela nativa.
Since 0.2.0-preview.29
Uma página guarda um FormController, declara os campos dela, e assina uma vez. Todo valor,
flag e erro chega por esse único evento, então nada na página rastreia estado uma segunda vez.
public sealed class SignUpPage : StatefulComponent
{
private readonly FormController _form = new();
public SignUpPage()
{
_form.Add("email", rules: [Rules.Required(), Rules.Email()]);
var password = _form.Add("password", rules: [Rules.Required(), Rules.MinLength(8)]);
_form.Add("confirm", rules: [Rules.Required(), Rules.Matches(password)]);
_form.Changed += () => SetState(() => { });
}
}Add devolve o campo, e é isso que torna possível uma regra entre campos: Rules.Matches(password)
guarda o outro campo e o lê na hora de validar, então julga o que está na tela em vez do que estava
lá quando o formulário foi montado.
Dois pares de propriedades carregam todo o comportamento de um formulário respeitoso, e cada par são duas perguntas diferentes:
| Pergunta | Serve para | |
|---|---|---|
Touched |
o usuário já SAIU deste campo? | se um erro pode ser mostrado |
Dirty |
o valor difere daquele com que o formulário abriu? | "descartar alterações?" |
Error |
o que está errado agora | sempre atual, mesmo antes de alguém digitar |
VisibleError |
o que o campo pode DIZER | ao que um componente se liga |
A consequência é o timing que dá para ver no sample: digite um endereço quebrado e nada fica vermelho, saia do campo e fica, corrija e o vermelho some na tecla que corrige. Um formulário renderizado no servidor chega calado pelo mesmo motivo — toda regra já rodou, e ninguém tocou em nada ainda.
var email = form.Field("email")!;
form.Set("email", "ana@");
email.Error; // "Enter a valid email address." — calculado na hora
email.VisibleError; // null — o cursor ainda está na caixa
form.Touch("email");
email.VisibleError; // "Enter a valid email address."Required, MinLength, MaxLength, Email, Range, Matches, e Custom para aquela que esta
lista não tem. Toda regra menos Required passa num valor vazio: um campo opcional em branco
não está mal formatado, está ausente — e "Digite um e-mail válido" embaixo de uma caixa opcional
vazia é o alarme falso mais comum em validação de formulário.
Uma regra é um predicado mais a mensagem dela, e é isso que a deixa cruzar para o browser:
System.ComponentModel.DataAnnotations valida por reflexão, e reflexão é exatamente o que o
transpilador não consegue levar.
form.Add("age", rules: [Rules.Range(18, 120, "You must be 18 or older.")]);
form.Add("slug", rules: [Rules.Custom("Lowercase letters and dashes only.",
value => value.All(c => char.IsAsciiLetterLower(c) || c == '-'))]);Since 0.2.0-preview.29
Formulário de verdade tem mais de um caminho por dentro, e a validação condicional chega como duas peças que se compõem, não como um segundo motor.
Rules.When torna qualquer regra condicional — ela envolve, então toda regra acima já é
condicional de graça. Enquanto a condição é falsa a regra vale vacuamente: nada foi pedido, então
o campo se cala em vez de guardar a última resposta que deu.
relevantWhen desliga o CAMPO inteiro. Um endereço de entrega num pedido que vai ser retirado na
loja não é um campo com regra que passa, é um campo que ninguém está perguntando: não guarda erro,
não pode invalidar o formulário, e a página pode simplesmente não desenhá-lo. O que foi digitado
sobrevive à pergunta sumir e voltar.
var kind = _form.Add("kind", "personal");
// A regra vai e volta; o campo fica.
_form.Add("taxNumber", rules: [Rules.When(() => kind.Value == "company", Rules.Required())]);
// O campo em si não está sendo perguntado enquanto a caixa está desmarcada.
_form.Add("phone", relevantWhen: () => _callMe,
rules: [Rules.Required("We need a number to call you on."), Rules.MinLength(9)]);
// …e a página o desenha só enquanto ele se aplica:
if (_form.Field("phone") is { Relevant: true })
card.Add(new FormInput(_form, "phone", "Phone"));Uma condição lê o que ela capturar. Quando isso é outro campo, nada mais é preciso — mudar
qualquer valor re-roda as regras de todos os outros campos. Quando é estado que o formulário não
possui (um checkbox na página, um plano escolhido num passo anterior), a página avisa com
Revalidate():
card.Add(new Checkbox(_callMe, () => SetState(() =>
{
_callMe = !_callMe;
_form.Revalidate(); // a condição mora fora do formulário, então o formulário é avisado
}), "Call me instead of emailing"));Since 0.2.0-preview.29
Dois componentes ligam o modelo aos pixels, e são finos de propósito. FormInput tem três fios e
mais nada — digitar chama Set, sair chama Touch, e o que ele desenha é VisibleError.
FormSubmit lê do controller todo o resto.
card.Add(new FormInput(_form, "email", "Email", placeholder: "you@example.com",
helper: "We never share it."));
card.Add(new FormInput(_form, "password", "Password", helper: "At least 8 characters")
{ Obscure = true });
var actions = new Row(gap: Space.S2);
actions.Add(new FormSubmit(_form, "Create account", Submit));
actions.Add(new Button("Reset", Variant.Ghost) { OnPressed = () => _form.Reset() });O botão de submit continua clicável enquanto o formulário é inválido, e essa escolha merece
defesa: um submit desabilitado é a forma mais comum de fazer um formulário parecer quebrado, porque
o campo que está errado costuma ser um que o usuário nunca visitou. Apertar é o que revela a
resposta — SubmitAsync toca em todos os campos primeiro.
SubmitAsync dá ao chamador três garantias que ele reimplementaria em toda página: um formulário
inválido nunca chega ao handler (em vez disso todo erro é revelado), um segundo submit é recusado
enquanto o primeiro está em voo (o clique duplo que cobra o cartão duas vezes), e um throw vira
SubmitError em vez de exceção não tratada — a rede falhar é coisa que acontece com formulário, não
um crash.
As regras do cliente são uma cortesia. A validação que conta roda onde os dados moram, e
ApplyServerErrors é como a resposta dela volta para o campo a que pertence:
[ServerAction]
public async Task<List<FieldError>> Register(string email) =>
await _users.Exists(email)
? [new FieldError("email", "That address is already registered.")]
: [];
private async Task Submit()
{
var verdict = await Register(_form.Field("email")!.Value);
if (verdict.Count > 0) { _form.ApplyServerErrors(verdict); return; }
_form.Accept(); // os valores atuais viram a nova base: nada sujo, nada gritando
}-
Ainda não há ponte de
[Required]/[EmailAddress]. Os atributos que um modelo já carrega vão ser lidos em tempo de BUILD e emitidos como chamadas para estas mesmas regras — até lá, as regras são escritas à mão. De propósito não existe um segundo motor de validação para o gerador mirar. - Mensagens são strings simples, não chaves de recurso. Um app que localiza as mensagens dele passa strings já localizadas, porque o resx é dele. Veja Localização para o chrome do próprio SDK, que é o único texto que este framework traduz por você.
- Componentes write-once: como uma única biblioteca de componentes alcança o browser e uma janela nativa.
- Design System: o input, o checkbox e o botão de que a superfície é feita.
-
Integração com o servidor:
[ServerAction], que é como um formulário alcança o servidor.
🌐 English · Português
🏁 Comece aqui
📱 Write-once
- Componentes write-once
- Superfície declarativa
- Motor Photon
- Design System
- Capacidades
- Armazenamento
- Formulários
- Editor de código
- Markdown
- Mermaid
- Renderização de Email
🏗️ Arquitetura
⚙️ Compilação
- Compilador
- Avaliação em tempo de compilação
- Recursos C# suportados
- Resolução de tipos externos
- Fluxo de build
- Diagnósticos
⚡ Runtime
🔌 Servidor
🎨 Ecossistema
🚀 Desenvolvimento