19 KiB
Construindo um App Bancário Parte 2: Criando um Formulário de Login e Registro
Quiz Pré-Aula
Introdução
Em quase todos os aplicativos web modernos, você pode criar uma conta para ter seu próprio espaço privado. Como múltiplos usuários podem acessar um aplicativo web ao mesmo tempo, é necessário um mecanismo para armazenar os dados pessoais de cada usuário separadamente e selecionar quais informações exibir. Não abordaremos como gerenciar identidade de usuário de forma segura, pois é um tópico extenso por si só, mas garantiremos que cada usuário possa criar uma (ou mais) conta bancária em nosso aplicativo.
Nesta parte, usaremos formulários HTML para adicionar login e registro ao nosso aplicativo web. Veremos como enviar os dados para uma API de servidor programaticamente e, por fim, como definir regras básicas de validação para os dados inseridos pelo usuário.
Pré-requisitos
Você precisa ter concluído a etapa de templates HTML e roteamento do aplicativo web para esta lição. Também é necessário instalar o Node.js e executar a API do servidor localmente para que você possa enviar dados e criar contas.
Atenção Você terá dois terminais rodando ao mesmo tempo, conforme listado abaixo:
- Para o aplicativo bancário principal que construímos na lição de templates HTML e roteamento
- Para a API do servidor do App Bancário que acabamos de configurar acima.
Você precisa que os dois servidores estejam funcionando para seguir o restante da lição. Eles escutam em portas diferentes (porta 3000
e porta 5000
), então tudo deve funcionar perfeitamente.
Você pode testar se o servidor está funcionando corretamente executando este comando em um terminal:
curl http://localhost:5000/api
# -> should return "Bank API v1.0.0" as a result
Formulário e controles
O elemento <form>
encapsula uma seção de um documento HTML onde o usuário pode inserir e enviar dados com controles interativos. Existem vários tipos de controles de interface de usuário (UI) que podem ser usados dentro de um formulário, sendo os mais comuns os elementos <input>
e <button>
.
Há muitos tipos diferentes de <input>
. Por exemplo, para criar um campo onde o usuário pode inserir seu nome de usuário, você pode usar:
<input id="username" name="username" type="text">
O atributo name
será usado como o nome da propriedade quando os dados do formulário forem enviados. O atributo id
é usado para associar um <label>
ao controle do formulário.
Confira a lista completa de tipos de
<input>
e outros controles de formulário para ter uma ideia de todos os elementos nativos de UI que você pode usar ao construir sua interface.
✅ Note que <input>
é um elemento vazio no qual você não deve adicionar uma tag de fechamento correspondente. No entanto, você pode usar a notação de auto-fechamento <input/>
, mas isso não é obrigatório.
O elemento <button>
dentro de um formulário é um pouco especial. Se você não especificar seu atributo type
, ele automaticamente enviará os dados do formulário ao servidor quando pressionado. Aqui estão os valores possíveis para o atributo type
:
submit
: O padrão dentro de um<form>
, o botão aciona a ação de envio do formulário.reset
: O botão redefine todos os controles do formulário para seus valores iniciais.button
: Não atribui um comportamento padrão ao botão quando pressionado. Você pode então atribuir ações personalizadas a ele usando JavaScript.
Tarefa
Vamos começar adicionando um formulário ao template de login
. Precisaremos de um campo para nome de usuário e um botão de Login.
<template id="login">
<h1>Bank App</h1>
<section>
<h2>Login</h2>
<form id="loginForm">
<label for="username">Username</label>
<input id="username" name="user" type="text">
<button>Login</button>
</form>
</section>
</template>
Se você observar mais de perto, perceberá que também adicionamos um elemento <label>
aqui. Elementos <label>
são usados para adicionar um nome aos controles de UI, como nosso campo de nome de usuário. Labels são importantes para a legibilidade dos formulários, mas também trazem benefícios adicionais:
- Ao associar um label a um controle de formulário, ele ajuda usuários que utilizam tecnologias assistivas (como leitores de tela) a entender quais dados são esperados.
- Você pode clicar no label para colocar o foco diretamente no controle associado, facilitando o acesso em dispositivos com tela sensível ao toque.
Acessibilidade na web é um tópico muito importante que muitas vezes é negligenciado. Graças aos elementos semânticos HTML, não é difícil criar conteúdo acessível se você os usar corretamente. Você pode ler mais sobre acessibilidade para evitar erros comuns e se tornar um desenvolvedor responsável.
Agora vamos adicionar um segundo formulário para o registro, logo abaixo do anterior:
<hr/>
<h2>Register</h2>
<form id="registerForm">
<label for="user">Username</label>
<input id="user" name="user" type="text">
<label for="currency">Currency</label>
<input id="currency" name="currency" type="text" value="$">
<label for="description">Description</label>
<input id="description" name="description" type="text">
<label for="balance">Current balance</label>
<input id="balance" name="balance" type="number" value="0">
<button>Register</button>
</form>
Usando o atributo value
, podemos definir um valor padrão para um determinado campo de entrada. Note também que o campo de entrada para balance
tem o tipo number
. Ele parece diferente dos outros campos? Experimente interagir com ele.
✅ Você consegue navegar e interagir com os formulários usando apenas o teclado? Como você faria isso?
Enviando dados para o servidor
Agora que temos uma interface funcional, o próximo passo é enviar os dados para o servidor. Vamos fazer um teste rápido usando nosso código atual: o que acontece se você clicar no botão Login ou Register?
Você notou a mudança na seção de URL do navegador?
A ação padrão de um <form>
é enviar os dados do formulário para o URL atual do servidor usando o método GET, anexando os dados do formulário diretamente ao URL. No entanto, esse método tem algumas limitações:
- Os dados enviados são muito limitados em tamanho (cerca de 2000 caracteres)
- Os dados são visíveis diretamente no URL (não é ideal para senhas)
- Não funciona com upload de arquivos
Por isso, você pode alterá-lo para usar o método POST, que envia os dados do formulário para o servidor no corpo da requisição HTTP, sem as limitações anteriores.
Embora o POST seja o método mais comumente usado para enviar dados, em alguns cenários específicos é preferível usar o método GET, como ao implementar um campo de busca.
Tarefa
Adicione as propriedades action
e method
ao formulário de registro:
<form id="registerForm" action="//localhost:5000/api/accounts" method="POST">
Agora tente registrar uma nova conta com seu nome. Após clicar no botão Register, você deve ver algo assim:
Se tudo correr bem, o servidor deve responder à sua requisição com uma resposta JSON contendo os dados da conta que foi criada.
✅ Tente registrar novamente com o mesmo nome. O que acontece?
Enviando dados sem recarregar a página
Como você provavelmente percebeu, há um pequeno problema com a abordagem que acabamos de usar: ao enviar o formulário, saímos do nosso aplicativo e o navegador redireciona para o URL do servidor. Estamos tentando evitar todos os recarregamentos de página com nosso aplicativo web, já que estamos criando um Aplicativo de Página Única (SPA).
Para enviar os dados do formulário ao servidor sem forçar um recarregamento da página, precisamos usar código JavaScript. Em vez de colocar um URL na propriedade action
de um elemento <form>
, você pode usar qualquer código JavaScript precedido pela string javascript:
para realizar uma ação personalizada. Usar isso também significa que você terá que implementar algumas tarefas que anteriormente eram feitas automaticamente pelo navegador:
- Recuperar os dados do formulário
- Converter e codificar os dados do formulário em um formato adequado
- Criar a requisição HTTP e enviá-la ao servidor
Tarefa
Substitua o action
do formulário de registro por:
<form id="registerForm" action="javascript:register()">
Abra o arquivo app.js
e adicione uma nova função chamada register
:
function register() {
const registerForm = document.getElementById('registerForm');
const formData = new FormData(registerForm);
const data = Object.fromEntries(formData);
const jsonData = JSON.stringify(data);
}
Aqui, recuperamos o elemento do formulário usando getElementById()
e usamos o helper FormData
para extrair os valores dos controles do formulário como um conjunto de pares chave/valor. Em seguida, convertemos os dados em um objeto regular usando Object.fromEntries()
e, finalmente, serializamos os dados para JSON, um formato comumente usado para troca de dados na web.
Os dados agora estão prontos para serem enviados ao servidor. Crie uma nova função chamada createAccount
:
async function createAccount(account) {
try {
const response = await fetch('//localhost:5000/api/accounts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: account
});
return await response.json();
} catch (error) {
return { error: error.message || 'Unknown error' };
}
}
O que essa função está fazendo? Primeiro, observe a palavra-chave async
aqui. Isso significa que a função contém código que será executado assincronamente. Quando usada junto com a palavra-chave await
, ela permite esperar que o código assíncrono seja executado - como esperar pela resposta do servidor aqui - antes de continuar.
Aqui está um vídeo rápido sobre o uso de async/await
:
🎥 Clique na imagem acima para assistir a um vídeo sobre async/await.
Usamos a API fetch()
para enviar dados JSON ao servidor. Este método recebe 2 parâmetros:
- O URL do servidor, então colocamos
//localhost:5000/api/accounts
aqui. - As configurações da requisição. É aqui que definimos o método como
POST
e fornecemos obody
para a requisição. Como estamos enviando dados JSON ao servidor, também precisamos definir o cabeçalhoContent-Type
comoapplication/json
para que o servidor saiba como interpretar o conteúdo.
Como o servidor responderá à requisição com JSON, podemos usar await response.json()
para analisar o conteúdo JSON e retornar o objeto resultante. Note que este método é assíncrono, então usamos a palavra-chave await
aqui antes de retornar para garantir que quaisquer erros durante a análise também sejam capturados.
Agora adicione algum código à função register
para chamar createAccount()
:
const result = await createAccount(jsonData);
Como usamos a palavra-chave await
aqui, precisamos adicionar a palavra-chave async
antes da função register:
async function register() {
Por fim, vamos adicionar alguns logs para verificar o resultado. A função final deve se parecer com esta:
async function register() {
const registerForm = document.getElementById('registerForm');
const formData = new FormData(registerForm);
const jsonData = JSON.stringify(Object.fromEntries(formData));
const result = await createAccount(jsonData);
if (result.error) {
return console.log('An error occurred:', result.error);
}
console.log('Account created!', result);
}
Foi um pouco longo, mas chegamos lá! Se você abrir as ferramentas de desenvolvedor do navegador e tentar registrar uma nova conta, não verá nenhuma mudança na página da web, mas uma mensagem aparecerá no console confirmando que tudo está funcionando.
✅ Você acha que os dados são enviados ao servidor de forma segura? E se alguém conseguir interceptar a requisição? Você pode ler sobre HTTPS para saber mais sobre comunicação segura de dados.
Validação de dados
Se você tentar registrar uma nova conta sem definir um nome de usuário primeiro, verá que o servidor retorna um erro com o código de status 400 (Bad Request).
Antes de enviar dados a um servidor, é uma boa prática validar os dados do formulário previamente, sempre que possível, para garantir que você está enviando uma requisição válida. Os controles de formulário HTML5 fornecem validação integrada usando vários atributos:
required
: o campo precisa ser preenchido, caso contrário o formulário não pode ser enviado.minlength
emaxlength
: definem o número mínimo e máximo de caracteres em campos de texto.min
emax
: definem o valor mínimo e máximo de um campo numérico.type
: define o tipo de dado esperado, comonumber
,email
,file
ou outros tipos integrados. Este atributo também pode alterar a renderização visual do controle de formulário.pattern
: permite definir um padrão de expressão regular para testar se os dados inseridos são válidos ou não. Dica: você pode personalizar a aparência dos seus controles de formulário dependendo se eles são válidos ou não, utilizando as pseudo-classes CSS:valid
e:invalid
.
Tarefa
Existem 2 campos obrigatórios para criar uma nova conta válida: o nome de usuário e a moeda. Os outros campos são opcionais. Atualize o HTML do formulário, utilizando tanto o atributo required
quanto o texto no rótulo do campo para que:
<label for="user">Username (required)</label>
<input id="user" name="user" type="text" required>
...
<label for="currency">Currency (required)</label>
<input id="currency" name="currency" type="text" value="$" required>
Embora esta implementação específica do servidor não imponha limites específicos no comprimento máximo dos campos, é sempre uma boa prática definir limites razoáveis para qualquer entrada de texto do usuário.
Adicione um atributo maxlength
aos campos de texto:
<input id="user" name="user" type="text" maxlength="20" required>
...
<input id="currency" name="currency" type="text" value="$" maxlength="5" required>
...
<input id="description" name="description" type="text" maxlength="100">
Agora, se você pressionar o botão Registrar e algum campo não atender a uma regra de validação que definimos, você verá algo como isto:
A validação como esta, realizada antes de enviar qualquer dado para o servidor, é chamada de validação do lado do cliente. Mas observe que nem sempre é possível realizar todas as verificações sem enviar os dados. Por exemplo, não podemos verificar aqui se já existe uma conta com o mesmo nome de usuário sem enviar uma solicitação ao servidor. Validações adicionais realizadas no servidor são chamadas de validação do lado do servidor.
Normalmente, ambas precisam ser implementadas, e enquanto a validação do lado do cliente melhora a experiência do usuário ao fornecer feedback instantâneo, a validação do lado do servidor é crucial para garantir que os dados do usuário que você manipula sejam consistentes e seguros.
🚀 Desafio
Exiba uma mensagem de erro no HTML se o usuário já existir.
Aqui está um exemplo de como a página de login final pode ficar após um pouco de estilização:
Questionário Pós-Aula
Revisão e Autoestudo
Os desenvolvedores têm sido muito criativos em seus esforços para criar formulários, especialmente no que diz respeito às estratégias de validação. Aprenda sobre diferentes fluxos de formulários explorando o CodePen; você consegue encontrar alguns formulários interessantes e inspiradores?
Tarefa
Estilize seu aplicativo bancário
Aviso Legal:
Este documento foi traduzido utilizando o serviço de tradução por IA Co-op Translator. Embora nos esforcemos para garantir a precisão, esteja ciente de que traduções automatizadas podem conter erros ou imprecisões. O documento original em seu idioma nativo deve ser considerado a fonte autoritativa. Para informações críticas, recomenda-se a tradução profissional realizada por humanos. Não nos responsabilizamos por quaisquer mal-entendidos ou interpretações equivocadas decorrentes do uso desta tradução.