You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Web-Dev-For-Beginners/translations/br/7-bank-project/3-data
leestott 3a46c7dc91
🌐 Update translations via Co-op Translator
3 weeks ago
..
README.md 🌐 Update translations via Co-op Translator 3 weeks ago
assignment.md 🌐 Update translations via Co-op Translator 4 weeks ago

README.md

Construir um App Bancário Parte 3: Métodos de Obtenção e Uso de Dados

Quiz Pré-Aula

Quiz pré-aula

Introdução

No núcleo de toda aplicação web está o dado. Os dados podem assumir muitas formas, mas seu principal propósito é sempre exibir informações para o usuário. Com os aplicativos web se tornando cada vez mais interativos e complexos, a forma como o usuário acessa e interage com as informações agora é uma parte essencial do desenvolvimento web.

Nesta lição, veremos como obter dados de um servidor de forma assíncrona e usar esses dados para exibir informações em uma página web sem recarregar o HTML.

Pré-requisitos

Você precisa ter construído a Formulário de Login e Registro da aplicação web para esta lição. Também é necessário instalar o Node.js e executar a API do servidor localmente para obter os dados da conta.

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

AJAX e obtenção de dados

Sites tradicionais atualizam o conteúdo exibido quando o usuário seleciona um link ou envia dados usando um formulário, recarregando a página HTML inteira. Toda vez que novos dados precisam ser carregados, o servidor web retorna uma nova página HTML que precisa ser processada pelo navegador, interrompendo a ação atual do usuário e limitando as interações durante o recarregamento. Esse fluxo de trabalho também é chamado de Aplicação Multi-Página ou MPA.

Fluxo de atualização em uma aplicação multi-página

Quando as aplicações web começaram a se tornar mais complexas e interativas, surgiu uma nova técnica chamada AJAX (JavaScript e XML Assíncronos). Essa técnica permite que os aplicativos web enviem e recuperem dados de um servidor de forma assíncrona usando JavaScript, sem precisar recarregar a página HTML, resultando em atualizações mais rápidas e interações mais suaves para o usuário. Quando novos dados são recebidos do servidor, a página HTML atual também pode ser atualizada com JavaScript usando a API DOM. Com o tempo, essa abordagem evoluiu para o que agora é chamado de Aplicação de Página Única ou SPA.

Fluxo de atualização em uma aplicação de página única

Quando o AJAX foi introduzido pela primeira vez, a única API disponível para obter dados de forma assíncrona era XMLHttpRequest. Mas os navegadores modernos agora também implementam a mais conveniente e poderosa Fetch API, que usa promessas e é mais adequada para manipular dados JSON.

Embora todos os navegadores modernos suportem a Fetch API, se você quiser que sua aplicação web funcione em navegadores antigos ou legados, é sempre uma boa ideia verificar a tabela de compatibilidade no caniuse.com primeiro.

Tarefa

Na lição anterior implementamos o formulário de registro para criar uma conta. Agora vamos adicionar código para fazer login usando uma conta existente e obter seus dados. Abra o arquivo app.js e adicione uma nova função login:

async function login() {
  const loginForm = document.getElementById('loginForm')
  const user = loginForm.user.value;
}

Aqui começamos recuperando o elemento do formulário com getElementById(), e então obtemos o nome de usuário do campo de entrada com loginForm.user.value. Cada controle de formulário pode ser acessado pelo seu nome (definido no HTML usando o atributo name) como uma propriedade do formulário.

De forma semelhante ao que fizemos para o registro, criaremos outra função para realizar uma solicitação ao servidor, mas desta vez para obter os dados da conta:

async function getAccount(user) {
  try {
    const response = await fetch('//localhost:5000/api/accounts/' + encodeURIComponent(user));
    return await response.json();
  } catch (error) {
    return { error: error.message || 'Unknown error' };
  }
}

Usamos a API fetch para solicitar os dados de forma assíncrona ao servidor, mas desta vez não precisamos de nenhum parâmetro extra além da URL a ser chamada, já que estamos apenas consultando dados. Por padrão, fetch cria uma solicitação HTTP GET, que é o que buscamos aqui.

encodeURIComponent() é uma função que escapa caracteres especiais para URLs. Que problemas poderíamos ter se não chamarmos essa função e usarmos diretamente o valor de user na URL?

Agora vamos atualizar nossa função login para usar getAccount:

async function login() {
  const loginForm = document.getElementById('loginForm')
  const user = loginForm.user.value;
  const data = await getAccount(user);

  if (data.error) {
    return console.log('loginError', data.error);
  }

  account = data;
  navigate('/dashboard');
}

Primeiro, como getAccount é uma função assíncrona, precisamos combiná-la com a palavra-chave await para aguardar o resultado do servidor. Como em qualquer solicitação ao servidor, também precisamos lidar com casos de erro. Por enquanto, adicionaremos apenas uma mensagem de log para exibir o erro e voltaremos a isso mais tarde.

Depois, precisamos armazenar os dados em algum lugar para que possamos usá-los posteriormente para exibir as informações do painel. Como a variável account ainda não existe, criaremos uma variável global para ela no topo do nosso arquivo:

let account = null;

Depois que os dados do usuário forem salvos em uma variável, podemos navegar da página de login para o dashboard usando a função navigate() que já temos.

Por fim, precisamos chamar nossa função login quando o formulário de login for enviado, modificando o HTML:

<form id="loginForm" action="javascript:login()">

Teste se tudo está funcionando corretamente registrando uma nova conta e tentando fazer login usando a mesma conta.

Antes de avançar para a próxima parte, também podemos completar a função register adicionando isto no final da função:

account = result;
navigate('/dashboard');

Você sabia que, por padrão, você só pode chamar APIs de servidor do mesmo domínio e porta que a página web que está visualizando? Este é um mecanismo de segurança imposto pelos navegadores. Mas espere, nossa aplicação web está rodando em localhost:3000 enquanto a API do servidor está rodando em localhost:5000, por que funciona? Usando uma técnica chamada Cross-Origin Resource Sharing (CORS), é possível realizar solicitações HTTP entre origens diferentes se o servidor adicionar cabeçalhos especiais à resposta, permitindo exceções para domínios específicos.

Saiba mais sobre APIs fazendo esta lição

Atualizar HTML para exibir dados

Agora que temos os dados do usuário, precisamos atualizar o HTML existente para exibi-los. Já sabemos como recuperar um elemento do DOM usando, por exemplo, document.getElementById(). Depois de ter um elemento base, aqui estão algumas APIs que você pode usar para modificá-lo ou adicionar elementos filhos a ele:

  • Usando a propriedade textContent, você pode alterar o texto de um elemento. Note que alterar este valor remove todos os filhos do elemento (se houver) e os substitui pelo texto fornecido. Assim, também é um método eficiente para remover todos os filhos de um elemento atribuindo uma string vazia '' a ele.

  • Usando document.createElement() junto com o método append(), você pode criar e anexar um ou mais novos elementos filhos.

Usando a propriedade innerHTML de um elemento, também é possível alterar seu conteúdo HTML, mas isso deve ser evitado, pois é vulnerável a ataques de cross-site scripting (XSS).

Tarefa

Antes de avançar para a tela do painel, há mais uma coisa que devemos fazer na página de login. Atualmente, se você tentar fazer login com um nome de usuário que não existe, uma mensagem é exibida no console, mas para um usuário comum nada muda e você não sabe o que está acontecendo.

Vamos adicionar um elemento de espaço reservado no formulário de login onde podemos exibir uma mensagem de erro, se necessário. Um bom lugar seria logo antes do botão de login <button>:

...
<div id="loginError"></div>
<button>Login</button>
...

Este elemento <div> está vazio, o que significa que nada será exibido na tela até adicionarmos algum conteúdo a ele. Também damos a ele um id para que possamos recuperá-lo facilmente com JavaScript.

Volte para o arquivo app.js e crie uma nova função auxiliar updateElement:

function updateElement(id, text) {
  const element = document.getElementById(id);
  element.textContent = text;
}

Esta função é bastante simples: dado um id de elemento e um texto, ela atualizará o conteúdo de texto do elemento DOM correspondente ao id. Vamos usar este método no lugar da mensagem de erro anterior na função login:

if (data.error) {
  return updateElement('loginError', data.error);
}

Agora, se você tentar fazer login com uma conta inválida, deverá ver algo assim:

Captura de tela mostrando a mensagem de erro exibida durante o login

Agora temos um texto de erro que aparece visualmente, mas se você tentar com um leitor de tela, perceberá que nada é anunciado. Para que o texto adicionado dinamicamente a uma página seja anunciado por leitores de tela, ele precisará usar algo chamado Região Viva. Aqui vamos usar um tipo específico de região viva chamado alerta:

<div id="loginError" role="alert"></div>

Implemente o mesmo comportamento para os erros da função register (não se esqueça de atualizar o HTML).

Exibir informações no painel

Usando as mesmas técnicas que acabamos de ver, também cuidaremos de exibir as informações da conta na página do painel.

Este é o formato de um objeto de conta recebido do servidor:

{
  "user": "test",
  "currency": "$",
  "description": "Test account",
  "balance": 75,
  "transactions": [
    { "id": "1", "date": "2020-10-01", "object": "Pocket money", "amount": 50 },
    { "id": "2", "date": "2020-10-03", "object": "Book", "amount": -10 },
    { "id": "3", "date": "2020-10-04", "object": "Sandwich", "amount": -5 }
  ],
}

Nota: para facilitar sua vida, você pode usar a conta test pré-existente que já está populada com dados.

Tarefa

Vamos começar substituindo a seção "Saldo" no HTML para adicionar elementos de espaço reservado:

<section>
  Balance: <span id="balance"></span><span id="currency"></span>
</section>

Também adicionaremos uma nova seção logo abaixo para exibir a descrição da conta:

<h2 id="description"></h2>

Como a descrição da conta funciona como um título para o conteúdo abaixo dela, ela é marcada semanticamente como um cabeçalho. Saiba mais sobre como a estrutura de cabeçalhos é importante para acessibilidade e analise criticamente a página para determinar o que mais poderia ser um cabeçalho.

Em seguida, criaremos uma nova função no app.js para preencher o espaço reservado:

function updateDashboard() {
  if (!account) {
    return navigate('/login');
  }

  updateElement('description', account.description);
  updateElement('balance', account.balance.toFixed(2));
  updateElement('currency', account.currency);
}

Primeiro, verificamos se temos os dados da conta necessários antes de prosseguir. Em seguida, usamos a função updateElement() que criamos anteriormente para atualizar o HTML.

Para deixar a exibição do saldo mais bonita, usamos o método toFixed(2) para forçar a exibição do valor com 2 dígitos após o ponto decimal.

Agora precisamos chamar nossa função updateDashboard() toda vez que a página do painel for carregada. Se você já terminou a tarefa da lição 1, isso deve ser simples, caso contrário, você pode usar a seguinte implementação.

Adicione este código ao final da função updateRoute():

if (typeof route.init === 'function') {
  route.init();
}

E atualize a definição de rotas com:

const routes = {
  '/login': { templateId: 'login' },
  '/dashboard': { templateId: 'dashboard', init: updateDashboard }
};

Com esta alteração, toda vez que a página do painel for exibida, a função updateDashboard() será chamada. Após um login, você deverá então ver o saldo da conta, a moeda e a descrição.

Criar linhas de tabela dinamicamente com templates HTML

Na primeira lição, usamos templates HTML junto com o método appendChild() para implementar a navegação em nosso aplicativo. Os templates também podem ser menores e usados para preencher partes repetitivas de uma página dinamicamente.

Usaremos uma abordagem semelhante para exibir a lista de transações na tabela HTML.

Tarefa

Adicione um novo template no <body> do HTML:

<template id="transaction">
  <tr>
    <td></td>
    <td></td>
    <td></td>
  </tr>
</template>

Este template representa uma única linha de tabela, com as 3 colunas que queremos preencher: data, objeto e quantia de uma transação.

Em seguida, adicione esta propriedade id ao elemento <tbody> da tabela dentro do template do painel para facilitar a localização usando JavaScript:

<tbody id="transactions"></tbody>

Nosso HTML está pronto, vamos mudar para o código JavaScript e criar uma nova função createTransactionRow:

function createTransactionRow(transaction) {
  const template = document.getElementById('transaction');
  const transactionRow = template.content.cloneNode(true);
  const tr = transactionRow.querySelector('tr');
  tr.children[0].textContent = transaction.date;
  tr.children[1].textContent = transaction.object;
  tr.children[2].textContent = transaction.amount.toFixed(2);
  return transactionRow;
}

Esta função faz exatamente o que seu nome sugere: usando o template que criamos anteriormente, ela cria uma nova linha de tabela e preenche seu conteúdo usando os dados da transação. Usaremos isso em nossa função updateDashboard() para preencher a tabela:

const transactionsRows = document.createDocumentFragment();
for (const transaction of account.transactions) {
  const transactionRow = createTransactionRow(transaction);
  transactionsRows.appendChild(transactionRow);
}
updateElement('transactions', transactionsRows);

Aqui usamos o método document.createDocumentFragment(), que cria um novo fragmento DOM no qual podemos trabalhar antes de finalmente anexá-lo à nossa tabela HTML.

Ainda há mais uma coisa que precisamos fazer antes que este código funcione, já que nossa função updateElement() atualmente suporta apenas conteúdo de texto. Vamos alterar um pouco seu código:

function updateElement(id, textOrNode) {
  const element = document.getElementById(id);
  element.textContent = ''; // Removes all children
  element.append(textOrNode);
}

Usamos o método append(), pois ele permite anexar tanto texto quanto Nodes DOM a um elemento pai, o que é perfeito para todos os nossos casos de uso. Se você tentar usar a conta test para fazer login, agora deverá ver uma lista de transações no painel 🎉.


🚀 Desafio

Trabalhem juntos para fazer a página do painel parecer um aplicativo bancário de verdade. Se você já estilizou seu aplicativo, tente usar media queries para criar um design responsivo que funcione bem tanto em dispositivos desktop quanto móveis.

Aqui está um exemplo de uma página de painel estilizada:

Captura de tela de um exemplo de resultado do painel após a estilização

Quiz Pós-Aula

Quiz pós-aula

Tarefa

Refatore e comente seu código


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.