Analog
On this page

Ações de Formulários no Servidor

O Analog suporta manipulação do lado do servidor para envios e validação de formulários.

Configurando o Formulário

Para manipular envios de formulários, use a diretiva FormAction do pacote @analogjs/router. A diretiva manipula a coleta do FormData e envia uma requisição POST para o servidor.

A diretiva emite após processar o formulário:

  • onSuccess: quando o formulário é processado no servidor e retorna uma resposta de sucesso.
  • onError: quando o formulário retorna uma resposta de erro.
  • onStateChange: quando o formulário é enviado.

A página de exemplo abaixo envia um e-mail para cadastro em newsletter.

// src/app/pages/newsletter.page.ts
type FormErrors =
  | {
      email?: string;
    }
  | undefined;

@Component({
  selector: 'app-newsletter-page',
  imports: [FormAction],
  template: `
    <h3>Newsletter Signup</h3>

    @if (!signedUp()) {
      <form
        method="post"
        (onSuccess)="onSuccess()"
        (onError)="onError($any($event))"
        (onStateChange)="errors.set(undefined)"
      >
        <div>
          <label for="email"> Email </label>
          <input type="email" name="email" />
        </div>

        <button class="button" type="submit">Submit</button>
      </form>

      @if (errors()?.email) {
        <p>{{ errors()?.email }}</p>
      }
    } @else {
      <div>Thanks for signing up!</div>
    }
  `,
})
export default class NewsletterComponent {
  signedUp = signal(false);
  errors = signal<FormErrors>(undefined);

  onSuccess() {
    this.signedUp.set(true);
  }

  onError(result?: FormErrors) {
    this.errors.set(result);
  }
}

A diretiva FormAction envia os dados do formulário para o servidor, que são processados pelo seu manipulador.

Manipulando a Ação do Formulário

Para manipular a ação do formulário, defina o arquivo .server.ts no mesmo diretório de .page.ts. O arquivo .serve.ts contém a função assíncrona action para processar o envio do formulário.

Na ação do servidor, você pode acessar variáveis de ambiente, ler cookies e executar outras operações exclusivas do lado do servidor.

// src/app/pages/newsletter.server.ts
export async function action({ event }: PageServerAction) {
  const body = await readFormData(event);
  const email = body.get('email') as string;

  if (!email) {
    return fail(422, { email: 'Email is required' });
  }

  if (email.length < 10) {
    return redirect('/');
  }

  return json({ type: 'success' });
}
  • A função json retorna uma resposta JSON.
  • A função redirect retorna uma resposta de redirecionamento para o cliente. Este deve ser um caminho absoluto.
  • A função fail é usada para retornar erros de validação de formulário.

Manipulando Múltiplos Formulários

Para manipular múltiplos formulários na mesma página, adicione um input oculto para distinguir cada formulário.

<form method="post">
  <div>
    <label for="email"> Email </label>
    <input type="email" name="email" />
  </div>

  <input type="hidden" name="action" value="register" />

  <button class="button" type="submit">Submit</button>
</form>

Na ação do servidor, use o valor de action.

export async function action({ event }: PageServerAction) {
  const body = await readFormData(event);
  const action = body.get('action') as string;

  if (action === 'register') {
    // processar formulário de registro
  }
}

Manipulando Requisições GET

Formulários com uma ação GET podem ser usados para navegar para a mesma URL, com os inputs do formulário passados como parâmetros de consulta.

O exemplo abaixo define um formulário de busca com o campo search como parâmetro de consulta.

// src/app/pages/search.page.ts
@Component({
  selector: 'app-search-page',
  imports: [FormAction],
  template: `
    <h3>Search</h3>

    <form method="get">
      <div>
        <label for="search"> Search </label>
        <input type="text" name="search" [value]="searchTerm()" />
      </div>

      <button class="button" type="submit">Submit</button>
    </form>

    @if (searchTerm()) {
      <p>Search Term: {{ searchTerm() }}</p>
    }
  `,
})
export default class NewsletterComponent {
  loader = toSignal(injectLoad<typeof load>(), { requireSync: true });
  searchTerm = computed(() => this.loader().searchTerm);
}

O parâmetro de consulta pode ser acessado através da ação do formulário no servidor.

// src/app/pages/search.server.ts
export async function load({ event }: PageServerLoad) {
  const query = getQuery(event);
  console.log('loaded search', query['search']);

  return {
    loaded: true,
    searchTerm: `${query['search']}`,
  };
}