> ## Documentation Index
> Fetch the complete documentation index at: https://ziva.zeleza.ru/llms.txt
> Use this file to discover all available pages before exploring further.

# Validation Showcase

> Rich input flows with built-in and custom validators

# Validation Showcase

This guide demonstrates how to build reliable forms with Ziva: cover every built-in validator, assemble validation chains, and plug in external checks.

## Queue with all built-in validators

```go theme={null}
func ShowBuiltInValidators() {
    v := ziva.DefaultValidators

    tasks := []ziva.Task{
        ziva.NewInputTask("Name", "Required field:").
            WithValidator(v.Required()),

        ziva.NewInputTask("Username", "3-20 characters, latin/number:").
            WithValidator(v.Username()),

        ziva.NewInputTask("Short description", "Up to 40 characters:").
            WithValidator(v.MaxLength(40)),

        ziva.NewInputTask("Comment", "At least 5 characters:").
            WithValidator(v.MinLength(5)),

        ziva.NewInputTask("Code", "Exactly 8 characters:").
            WithValidator(v.Length(8)),

        ziva.NewInputTask("Email", "Email address:").
            WithInputType(ziva.InputTypeEmail).
            WithValidator(v.Email()),

        ziva.NewInputTask("Website", "URL (optional):").
            WithValidator(v.OptionalURL()),

        ziva.NewInputTask("IP", "IPv4 or IPv6:").
            WithInputType(ziva.InputTypeIP).
            WithValidator(v.IP()),

        ziva.NewInputTask("Domain", "Domain name:").
            WithInputType(ziva.InputTypeDomain).
            WithValidator(v.Domain()),

        ziva.NewInputTask("Port", "Range 1-65535:").
            WithInputType(ziva.InputTypeNumber).
            WithValidator(v.Port()),

        ziva.NewInputTask("Number", "Range 1-100:").
            WithInputType(ziva.InputTypeNumber).
            WithValidator(v.Range(1, 100)),

        ziva.NewInputTask("Alphanumeric", "Letters and digits only:").
            WithValidator(v.AlphaNumeric()),

        ziva.NewInputTask("Password", "Minimum 8 characters:").
            WithInputType(ziva.InputTypePassword).
            WithValidator(v.StandardPassword()),

        ziva.NewInputTask("Strong password", "Minimum 12 characters, mixed set:").
            WithInputType(ziva.InputTypePassword).
            WithValidator(v.StrongPassword()),
    }

    queue := ziva.NewQueue("Validation tour").
        WithTasksNumbered(true, "[%02d]")
    queue.AddTasks(tasks...)
    _ = queue.Run()
}
```

<Info>
  Each task triggers its validator after the user presses <kbd>Enter</kbd>. When validation fails, the TUI shows the error and keeps the task focused until the value matches the rules.
</Info>

## Custom validation chain

```go theme={null}
type Chain struct {
    validators []ziva.Validator
}

func (c Chain) Validate(input string) error {
    for _, validator := range c.validators {
        if err := validator.Validate(input); err != nil {
            return err
        }
    }
    return nil
}

func (c Chain) Description() string {
    parts := make([]string, len(c.validators))
    for i, validator := range c.validators {
        parts[i] = validator.Description()
    }
    return strings.Join(parts, "; ")
}

func AskSecureToken() {
    v := ziva.DefaultValidators

    token := ziva.NewInputTask(
        "API token",
        "Required, 16-64 characters, alphanumeric",
    ).WithValidator(Chain{
        validators: []ziva.Validator{
            v.Required(),
            v.MinLength(16),
            v.MaxLength(64),
            v.AlphaNumeric(),
        },
    })

    queue := ziva.NewQueue("Token validation")
    queue.AddTasks(token)
    _ = queue.Run()

    fmt.Println("Token accepted:", token.GetValue())
}
```

## External validator example

```go theme={null}
func emailNotInCRM(email string) bool {
    // Replace with actual API call.
    return !strings.HasSuffix(email, "@banned.example")
}

type UniqueEmail struct{}

func (UniqueEmail) Validate(input string) error {
    if !emailNotInCRM(input) {
        return errors.New("email is already registered")
    }
    return nil
}

func (UniqueEmail) Description() string {
    return "Email must be unique in CRM"
}

func SignUpForm() error {
    v := ziva.DefaultValidators

    email := ziva.NewInputTask("Email", "Enter work email:").
        WithInputType(ziva.InputTypeEmail).
        WithValidator(v.Email())

    password := ziva.NewInputTask(
        "Password",
        "Minimum 12 characters, letters/digits/symbols",
    ).WithInputType(ziva.InputTypePassword).
        WithValidator(v.StrongPassword())

    confirm := ziva.NewYesNoTask(
        "Confirmation",
        "Submit data to CRM?",
    ).WithDefaultYes()

    queue := ziva.NewQueue("User signup")
    queue.AddTasks(email, password, confirm)
    if err := queue.Run(); err != nil {
        return err
    }

    check := ziva.NewInputTask("Uniqueness check", "Email must be unique:").
        WithInputType(ziva.InputTypeEmail).
        WithValidator(UniqueEmail{})

    followUp := ziva.NewQueue("CRM check")
    followUp.AddTasks(check)
    return followUp.Run()
}
```

<Note>
  Use a `FuncTask` after collecting input if you need to call an external service and display the outcome (success, warning, retry hints) inside the TUI.
</Note>

## Quick tips

* Keep `Description()` human-friendly — Ziva shows it near the input field.
* Combine `WithAllowEmpty(true)` with validators that handle empty strings correctly.
* Break long forms into several queues; it simplifies returning to the problem field.
* Write unit tests for elaborate validators — they are plain Go code.

Head over to the best practices page to see how these building blocks fit together in real projects.
