# How to validate a text field

> Restrict characters, limit length, and read the clean value before submitting.

Source: https://crossfly.dev/docs/cookbook/forms/validar-campo — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# How to validate a text field

**Purpose.** Ensure the user can only type valid characters, limit the number of characters, and get the value without a mask at submission time. In CrossFly, input validation is declarative (done in the field's style), and business validation is done on submit.

## APIs used
- `campo.<TextFieldStyle>getStyle().validChars("0123456789")` — filters the accepted characters. `null` (default) accepts everything. The comparison is done in UPPERCASE: internally `isCharValid(c)` does `validChars.indexOf(Character.toUpperCase(c))`, so provide the letters in uppercase even if the field accepts lowercase.
- `campo.<TextFieldStyle>getStyle().maxLength(11)` — limits the total number of characters (`0` = unlimited). Applies to typing and to pasting (`clipboardPaste`).
- `campo.<TextFieldStyle>getStyle().capitalise(Capitalise.UPPER)` — forces UPPERCASE/lowercase (`UPPER`, `LOWER`, `NORMAL`).
- `TextField.getText()` — text as displayed (with mask, if any).
- `TextField.getTextWithoutMask()` — only the digits/raw content (e.g., `"12345678900"` instead of `"123.456.789-00"`). Use this on submit.
- `TextInput.mapKeys(from, to)` — remaps keys (e.g., replace a comma with a dot). `from` and `to` must have the same length.

> There is no per-key callback in CrossFly. There is no `onTextChanged` — you read `getText()`/`getTextWithoutMask()` at submission time (or inside the button's `onAction`).

## Complete example (copy, paste, and run)
```java
package com.exemplo;

import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;
import dev.crossfly.ui.style.*;   // TextFieldStyle (config on the style)

/** Runnable main screen. Point the launcher (classname) and run: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      TextField cpf = new TextField("");
      cpf.caption("CPF");
      cpf.<TextFieldStyle>getStyle()
         .validChars("0123456789")   // digits only
         .maxLength(11);             // 11 digits

      TextField apelido = new TextField("");
      apelido.caption("Apelido");
      apelido.<TextFieldStyle>getStyle()
         .capitalise(Capitalise.LOWER)
         .maxLength(20);

      Label resultado = new Label("");

      Button enviar = new Button("Enviar");
      enviar.onAction(origin ->
      {
         String cpfLimpo = cpf.getTextWithoutMask();   // "12345678900"
         if (cpfLimpo.length() != 11)
         {
            Hint.show("CPF deve ter 11 dígitos", 2000);
            return;
         }
         resultado.setText("CPF: " + cpfLimpo + " · apelido: " + apelido.getText());
      });

      add(LAY_FILL, cpf);
      add(LAY_FILL, apelido);
      add(LAY_AUTO, enviar);
      add(LAY_FILL, resultado);
   }
}
```

> The snippet below shows **only the core**. To run it, place it in the `MainWindow` skeleton above.

## Minimal snippet
```java
import dev.crossfly.ui.*;
import dev.crossfly.ui.style.*;
import static dev.crossfly.ui.UIConstants.Capitalise;

TextField cpf = new TextField("");
cpf.caption("CPF");
cpf.<TextFieldStyle>getStyle()
   .validChars("0123456789")   // digits only
   .maxLength(11);             // 11 digits

TextField apelido = new TextField("");
apelido.caption("Apelido");
apelido.<TextFieldStyle>getStyle()
   .capitalise(Capitalise.LOWER)
   .maxLength(20);

Button enviar = new Button("Enviar");
enviar.onAction(origin ->
{
   String cpfLimpo = cpf.getTextWithoutMask();   // "12345678900"
   if (cpfLimpo.length() != 11)
   {
      new Alert("Erro", "CPF deve ter 11 dígitos").show();
      return;
   }
   // ... proceeds with submit
});
```

## Dig deeper
- [Masks and keyboards](/docs/ui/forms-and-input/masks-and-keyboards) · [Focus and validation](/docs/ui/forms-and-input/focus-and-validation)
- Sibling recipes: [CPF/ZIP/date mask](/docs/cookbook/forms/mascara-cpf) · [Password field](/docs/cookbook/forms/campo-de-senha)
- Canonical source: Chapter 14 — Basic widgets · [Enums (UIConstants)](/docs/reference/enums-uiconstants)
