Registos e persistência: guardar no browser

Até agora, bastava carregar em F5 para os clientes desaparecerem. Hoje o CRM passa a guardar os dados no browser e vais perceber como se organiza uma base de dados: tabelas, registos, campos e chave primária.

🎯 No fim desta aula vais conseguir

🧰 Antes de começar

  1. Abre a pasta meu-crm no VS Code (Ficheiro → Abrir Pasta…).
  2. Abre o index.html com o Live Server (botão direito → Open with Live Server).
  3. Acrescenta 2 clientes e carrega em F5. Desapareceram, certo? É esse o problema que vais resolver hoje.

😕 Faltaste à aula 7 ou o teu projeto não funciona? Descarrega o ponto de controlo da aula 7, descompacta-o e trabalha a partir dele.

💡 Fizeste o E1 da aula 2 (campo Cidade)? Então o cabeçalho da tabela tem uma coluna «Cidade», mas as linhas não têm essa célula, e as colunas aparecem desalinhadas. Não faz mal: vais completar a Cidade na aula 12. Se te faz confusão, apaga por agora só a linha <th>Cidade</th> do index.html. Não apagues o campo Cidade do formulário: vais precisar dele.

Parte A — Como se organizam os dados (≈ 25 min)

1 Tabelas, registos e campos

Pensa numa folha de Excel com os clientes de uma empresa. Uma base de dados organiza a informação exatamente assim:

id 🔑nomeempresaestadocriadoEm
1759480001000Ana SilvaPadaria SilvaCliente2026-10-03
1759480002000Rui CostaOficina CostaLead2026-10-03
1759480003000Ana SilvaFlorista AnaLead2026-10-03

💡 No teu CRM: a lista clientes é a tabela, cada objeto { … } é um registo e cada propriedade (nome, email…) é um campo. Hoje vais acrescentar o campo id (chave primária) e o campo criadoEm (data de criação).

🤔 Pensa: olha para a tabela de cima. Porque é que o nome não serve de chave primária? E o email, servia sempre? Escreve a tua resposta no caderno.

2 A gaveta do browser: localStorage

Cada browser tem, para cada site, uma pequena gaveta chamada localStorage. Lá dentro guardas textos com uma etiqueta (a chave). O que está na gaveta não se perde com F5, nem quando fechas o browser.

Há uma regra importante: a gaveta só guarda texto.

Abre o CRM, abre a Consola (F12 → separador Consola/Console) e escreve estas linhas, uma de cada vez, carregando em Enter no fim de cada uma:

Agora carrega em F5 e volta a escrever localStorage.getItem('cor'); na Consola.

✅ Deves ver: 'azul', antes e depois do F5. O texto ficou guardado na gaveta!

Para arrumar, apaga a etiqueta de teste: localStorage.removeItem('cor');

3 JSON: escrever objetos como texto

Temos um problema: os nossos clientes são objetos dentro de uma lista, mas a gaveta só aceita texto. A solução chama-se JSON: uma forma de escrever objetos e listas como texto.

🤔 Prevê antes de executar: o que vai aparecer quando escreveres texto na Consola? E typeof texto?

Escreve na Consola, uma linha de cada vez:

✅ Deves ver: texto mostra '{"nome":"Ana","estado":"Lead","compras":3}' (os nomes ficam entre aspas e tudo junto numa linha); typeof texto dá 'string' (é texto); deVolta.nome dá 'Ana' (voltou a ser um objeto).

🔎 Investiga (Parte A)

  1. Na Consola, escreve JSON.stringify({ nome: 'Ana' }). O que muda em relação ao que escreveste? Repara nas aspas.
  2. Escreve JSON.stringify([10, 20, 30]). Uma lista também se transforma em texto?

Parte B — O CRM passa a guardar os clientes (≈ 45 min)

4 A camada de acesso a dados: o dados.js

Vamos pôr todo o código que lê e guarda dados num ficheiro à parte: o dados.js. O app.js trata do ecrã (botões, tabela, mensagens); o dados.js trata só dos dados.

Porquê separar? Pensa nos arquivadores de fichas de uma empresa: os funcionários pedem as fichas ao arquivista e não querem saber se os arquivadores estão na cave ou noutro edifício. Se um dia trocarmos o localStorage por um servidor de base de dados a sério (como o MySQL), só muda o dados.js: o resto do CRM continua igual. A isto chama-se arquitetura de componentes de acesso a dados.

O bloco tem três funções:

Grava (Ctrl+S).

💡 O index.html já liga o dados.js desde a aula 6, antes do app.js. A ordem importa: o app.js vai usar funções do dados.js, por isso o dados.js tem de ser lido primeiro.

5 Ler os clientes quando a página abre

Até agora a lista começava sempre vazia (let clientes = [];). A partir de hoje começa com o que estiver guardado na gaveta.

⚠️ Atenção: este bloco substitui código antigo. No app.js, por baixo de AULA 7: COLAR AQUI, seleciona as 3 linhas que começam em // A nossa "tabela" de clientes e terminam em let clientes = [];, apaga-as e cola este bloco no lugar delas. Se ficarem as duas versões, aparece o erro Identifier 'clientes' has already been declared.

6 Guardar com chave primária e data

A nova versão do guardarCliente() tem três novidades:

⚠️ Atenção: este bloco substitui a função antiga. No app.js, seleciona a função guardarCliente da aula 7 inteira (desde function guardarCliente até à } que a fecha, incluindo o comentário por cima), apaga-a e cola esta no mesmo sítio. Não pode ficar nenhuma guardarCliente antiga!

7 Testar: o F5 já não apaga nada

  1. Grava todos os ficheiros e volta ao CRM no browser.
  2. Acrescenta 3 clientes diferentes.
  3. Carrega em F5.
  4. Fecha o separador do browser e volta a abrir o CRM com o Live Server.

✅ Deves ver: os 3 clientes continuam na tabela depois do F5 e depois de fechares e abrires o browser. O teu CRM já tem persistência: os dados ficam guardados.

😕 Não funciona?
  • Abre a Consola (F12). carregar is not defined → o dados.js não foi gravado (Ctrl+S) ou colaste o bloco do passo 4 no ficheiro errado.
  • Identifier 'clientes' has already been declared → ficou o let clientes = []; antigo. Volta ao passo 5.
  • Os clientes desaparecem com F5 e não há erros → ficaste com duas funções guardarCliente (vale a última, que é a antiga). Procura com Ctrl+F e apaga a que não tem guardar('clientes', clientes).
  • No dia seguinte os clientes «desapareceram» → abriste o CRM noutro endereço. A gaveta é diferente para cada browser e para cada endereço: http://127.0.0.1:5500 e http://localhost:5500 são gavetas diferentes! Abre sempre da mesma maneira.

8 Espreitar a gaveta no DevTools

Os teus dados estão guardados no browser e podes vê-los:

  1. Carrega em F12 e abre o separador Aplicação (em inglês, Application). Se não o vires, clica em » para mostrar mais separadores.
  2. Na coluna da esquerda, em Armazenamento, abre Armazenamento local (Local storage) e clica no endereço do Live Server (http://127.0.0.1:5500).
  3. Clica na linha com a chave crm_clientes.

✅ Deves ver: à direita, a chave crm_clientes com um texto JSON comprido: [{"id":17…,"nome":"…",…}]. Na parte de baixo, o mesmo texto aparece «arrumado» e podes abrir cada registo clicando nas setas ▸.

🔎 Investiga (Parte B)

  1. No separador Aplicação, clica com o botão direito na chave crm_clientes e escolhe Eliminar (Delete). Depois carrega em F5. O que acontece à tabela? Porquê?
  2. Abre o teu CRM noutro browser (por exemplo, Edge em vez de Chrome) com o mesmo endereço. Os teus clientes estão lá? Onde é que os dados ficam guardados, afinal?
  3. Na Consola, escreve JSON.stringify({ nome: 'Ana' }) e depois JSON.stringify(clientes). Reconheces o texto que viste no separador Aplicação?
  4. Escreve Date.now() na Consola várias vezes seguidas. O número muda? Porque é que serve para criar um id diferente para cada cliente?

✍️ Exercícios (fazem parte do teu projeto)

Estes exercícios não têm o código pronto: usa o que aprendeste hoje.

E1 · Detetive de dados (sem código)

Cria pelo menos 3 clientes. Depois, no separador Aplicação do DevTools, observa a chave crm_clientes e responde no caderno:

  1. Quantos registos tem a chave crm_clientes? Como os contaste?
  2. Que campos tem cada registo? Escreve os nomes de todos.
  3. Qual é o id do primeiro cliente? Quantos algarismos tem?
  4. Em que formato está a data criadoEm? Escreve um exemplo e explica a ordem (dia, mês, ano?).

💡 Dica: cada registo começa com { e acaba com }. Na parte de baixo do separador Aplicação os dados aparecem arrumados, com um número (0, 1, 2…) para cada registo.

E2 · Botão «Apagar todos os clientes»

Por baixo da tabela, cria um botão «⚠️ Apagar todos os clientes». Ao clicar, pergunta ao utilizador se tem a certeza. Se ele confirmar, apaga todos os clientes, também da gaveta (depois de F5 não podem voltar a aparecer).

💡 Dica: confirm('Tens a certeza?') mostra uma janela com OK e Cancelar e devolve true ou false. Para esvaziar a lista: clientes = [];. Depois de esvaziar, o que tens de fazer para que fique gravado? E para que a tabela se atualize? (Olha para o que faz o guardarCliente depois do push.) Para o botão, inspira-te no botão Guardar (usa class="secundario" para ficar cinzento).

⭐ Desafio · Clientes de exemplo automáticos

Quando a página abre e a lista de clientes está vazia, cria automaticamente 3 clientes de exemplo (com nome, empresa, email, telefone, NIF, estado, id e criadoEm) e grava-os.

💡 Dica: cria uma função criarExemplos() que faz três vezes clientes.push({ … }) (usa novoId() para o id) e no fim grava a lista. No arranque (no fim do app.js), antes de atualizarEcra();, só chamas a função se clientes.length === 0. Isto vai ser muito útil na aula 19, quando publicares o CRM na Internet.

📦 O que mostrar ao professor

⏱️ Se sobrar tempo

🤖 Pedir ajuda à IA (opcional)

Se não percebeste bem o JSON ou o localStorage, podes pedir a uma IA gratuita que te explique com exemplos, sem te dar código para o projeto. Copia este pedido, troca o texto entre [ ] e cola-o na IA:

📖 Palavras novas

Tabela
Conjunto de registos do mesmo tipo, como uma folha de Excel (ex.: a tabela de clientes).
Registo
Uma linha da tabela: toda a informação sobre um cliente.
Campo
Uma coluna da tabela: um tipo de informação (nome, email, estado…).
Chave primária
Campo com um valor diferente em todos os registos, que identifica cada um (no CRM, o id).
Persistência
Os dados continuam guardados depois de fechar ou recarregar a página.
localStorage
A «gaveta» do browser onde se guardam textos com uma etiqueta (chave). Cada browser e cada endereço tem a sua.
JSON
Forma de escrever objetos e listas como texto, ex.: {"nome":"Ana"}.
JSON.stringify / JSON.parse
Transformam um objeto em texto / um texto em objeto.
Camada de acesso a dados
A parte do programa que só trata de ler e guardar dados (no CRM, o dados.js).