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/pt/7-bank-project/3-data
Lee Stott 2daab5271b
Update Quiz Link
3 weeks ago
..
README.md Update Quiz Link 3 weeks ago
assignment.md 🌐 Update translations via Co-op Translator 4 weeks ago

README.md

Construir uma App Bancária Parte 3: Métodos de Obtenção e Utilização de Dados

Questionário Pré-Aula

Questionário pré-aula

Introdução

No núcleo de cada aplicação web está dados. Os dados podem assumir muitas formas, mas o seu principal objetivo é sempre exibir informações ao utilizador. Com as aplicações web a tornarem-se cada vez mais interativas e complexas, a forma como o utilizador acede e interage com as informações tornou-se uma parte essencial do desenvolvimento web.

Nesta lição, veremos como obter dados de um servidor de forma assíncrona e utilizá-los para exibir informações numa página web sem recarregar o HTML.

Pré-requisitos

É necessário ter construído a Formulário de Login e Registo 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.

Pode testar se o servidor está a funcionar corretamente executando este comando num terminal:

curl http://localhost:5000/api
# -> should return "Bank API v1.0.0" as a result

AJAX e obtenção de dados

Os sites tradicionais atualizam o conteúdo exibido quando o utilizador seleciona um link ou submete dados através de um formulário, recarregando a página HTML completa. Sempre que novos dados precisam de ser carregados, o servidor web retorna uma nova página HTML que precisa de ser processada pelo navegador, interrompendo a ação atual do utilizador e limitando as interações durante o recarregamento. Este fluxo de trabalho também é chamado de Aplicação Multi-Página ou MPA.

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

Quando as aplicações web começaram a tornar-se mais complexas e interativas, surgiu uma nova técnica chamada AJAX (JavaScript e XML Assíncronos). Esta técnica permite que as aplicações web enviem e obtenham dados de um servidor de forma assíncrona usando JavaScript, sem ter de recarregar a página HTML, resultando em atualizações mais rápidas e interações mais suaves. 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, esta abordagem evoluiu para o que agora é chamado de Aplicação de Página Única ou SPA.

Fluxo de atualização numa 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 utiliza promessas e é mais adequada para manipular dados JSON.

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

Tarefa

Na lição anterior implementámos o formulário de registo para criar uma conta. Agora vamos adicionar código para fazer login usando uma conta existente e obter os seus dados. Abra o ficheiro 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 por obter o elemento do formulário com getElementById(), e depois obtemos o nome de utilizador do campo de entrada com loginForm.user.value. Cada controlo de formulário pode ser acedido pelo seu nome (definido no HTML usando o atributo name) como uma propriedade do formulário.

De forma semelhante ao que fizemos para o registo, vamos criar 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' };
  }
}

Utilizamos a API fetch para solicitar os dados de forma assíncrona ao servidor, mas desta vez não precisamos de parâmetros adicionais além do URL a ser chamado, pois estamos apenas a consultar dados. Por padrão, fetch cria uma solicitação HTTP GET, que é o que procuramos aqui.

encodeURIComponent() é uma função que escapa caracteres especiais para URLs. Que problemas poderíamos ter se não chamássemos esta função e utilizássemos diretamente o valor de user no URL?

Agora vamos atualizar a 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 de combiná-la com a palavra-chave await para aguardar o resultado do servidor. Como em qualquer solicitação ao servidor, também temos de lidar com casos de erro. Por enquanto, vamos apenas adicionar uma mensagem de log para exibir o erro e voltar a ele mais tarde.

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

let account = null;

Depois de os dados do utilizador serem guardados numa variável, podemos navegar da página de login para o dashboard usando a função navigate() que já temos.

Finalmente, precisamos de chamar a nossa função login quando o formulário de login for submetido, modificando o HTML:

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

Teste se tudo está a funcionar corretamente registando uma nova conta e tentando fazer login com 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');

Sabia que, por padrão, só pode chamar APIs de servidor a partir do mesmo domínio e porta da página web que está a visualizar? Este é um mecanismo de segurança imposto pelos navegadores. Mas espere, a nossa aplicação web está a funcionar em localhost:3000, enquanto a API do servidor está a funcionar 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 ao fazer esta lição

Atualizar HTML para exibir dados

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

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

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

Usando a propriedade innerHTML de um elemento, também é possível alterar os seus conteúdos HTML, mas esta deve ser evitada, pois é vulnerável a ataques de cross-site scripting (XSS).

Tarefa

Antes de avançar para o ecrã do painel (dashboard), há mais uma coisa que devemos fazer na página de login. Atualmente, se tentar fazer login com um nome de utilizador que não existe, uma mensagem é exibida na consola, mas para um utilizador normal nada muda e não sabe o que está a acontecer.

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 no ecrã até adicionarmos algum conteúdo a ele. Também damos-lhe um id para que possamos recuperá-lo facilmente com JavaScript.

Volte ao ficheiro 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 com o id correspondente. 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 tentar fazer login com uma conta inválida, deverá ver algo como isto:

Captura de ecrã mostrando a mensagem de erro exibida durante o login

Agora temos texto de erro que aparece visualmente, mas se tentar com um leitor de ecrã, notará que nada é anunciado. Para que o texto adicionado dinamicamente a uma página seja anunciado por leitores de ecrã, será necessário 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 (dashboard)

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

Este é o aspeto 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 a sua vida, pode usar a conta test pré-existente que já está preenchida com dados.

Tarefa

Vamos começar por substituir a secção "Saldo" no HTML para adicionar elementos de espaço reservado:

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

Também vamos adicionar uma nova secçã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, está marcada semanticamente como um cabeçalho. Saiba mais sobre como a estrutura de cabeçalhos é importante para a acessibilidade e analise criticamente a página para determinar o que mais poderia ser um cabeçalho.

Em seguida, vamos criar uma nova função em 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 de que precisamos antes de prosseguir. Depois, usamos a função updateElement() que criámos anteriormente para atualizar o HTML.

Para tornar 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 de chamar a nossa função updateDashboard() sempre que o painel for carregado. Se já terminou o exercício da lição 1, isto deve ser simples, caso contrário 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 das rotas com:

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

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

Criar linhas de tabela dinamicamente com modelos HTML

Na primeira lição, usamos modelos HTML juntamente com o método appendChild() para implementar a navegação na nossa aplicação. Os modelos também podem ser menores e usados para preencher partes repetitivas de uma página de forma dinâmica.

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

Tarefa

Adicione um novo modelo no <body> do HTML:

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

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

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

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

O 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 o seu nome indica: usando o modelo que criámos anteriormente, cria uma nova linha de tabela e preenche os seus conteúdos usando os dados da transação. Vamos usá-la na 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 temos de fazer antes que este código funcione, já que a nossa função updateElement() atualmente suporta apenas conteúdo de texto. Vamos alterar o seu código um pouco:

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

Usamos o método append(), pois permite anexar texto ou Nodos DOM a um elemento pai, o que é perfeito para todos os nossos casos de uso. Se tentar usar a conta test para iniciar sessão, deverá agora ver uma lista de transações no painel 🎉.


🚀 Desafio

Trabalhem juntos para fazer com que a página do painel se pareça com uma aplicação bancária real. Se já estilizou a sua aplicação, tente usar media queries para criar um design responsivo que funcione bem tanto em dispositivos desktop como móveis.

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

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

Questionário Pós-Aula

Questionário pós-aula

Tarefa

Refatore e comente o 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, é importante notar que traduções automáticas podem conter erros ou imprecisões. O documento original na sua língua nativa deve ser considerado a fonte autoritária. 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 incorretas decorrentes do uso desta tradução.