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
- Explicar o que é uma tabela, um registo, um campo e uma chave primária.
- Guardar e ler texto no localStorage, a «gaveta» do browser.
- Transformar listas e objetos em texto JSON e voltar atrás (
JSON.stringifyeJSON.parse). - Criar a camada de acesso a dados do CRM (o ficheiro
dados.js) para os clientes não se perderem. - Ver os dados guardados nas ferramentas de programador do browser (DevTools).
🧰 Antes de começar
- Abre a pasta
meu-crmno VS Code (Ficheiro → Abrir Pasta…). - Abre o
index.htmlcom o Live Server (botão direito → Open with Live Server). - 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:
- A folha inteira é uma tabela (a tabela clientes).
- Cada linha é um registo: tudo o que se sabe sobre um cliente.
- Cada coluna é um campo: um tipo de informação (nome, email, estado…).
- A coluna
idé a chave primária: um número que é diferente em todas as linhas e que identifica cada registo sem confusões, como o teu número de aluno ou o NIF de uma pessoa.
| id 🔑 | nome | empresa | estado | criadoEm |
|---|---|---|---|---|
| 1759480001000 | Ana Silva | Padaria Silva | Cliente | 2026-10-03 |
| 1759480002000 | Rui Costa | Oficina Costa | Lead | 2026-10-03 |
| 1759480003000 | Ana Silva | Florista Ana | Lead | 2026-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.
JSON.stringify(objeto)transforma um objeto (ou uma lista) em texto. É como dobrar uma ficha para caber num envelope.JSON.parse(texto)faz o contrário: transforma o texto outra vez num objeto. É desdobrar a ficha.
🤔 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)
- Na Consola, escreve
JSON.stringify({ nome: 'Ana' }). O que muda em relação ao que escreveste? Repara nas aspas. - 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:
carregar('clientes')— vai à gaveta buscar o texto com a etiquetacrm_clientese transforma-o numa lista comJSON.parse. Se a gaveta ainda estiver vazia, devolve uma lista vazia[].guardar('clientes', lista)— transforma a lista em texto comJSON.stringifye guarda-a na gaveta com a etiquetacrm_clientes.novoId()— cria um número que nunca se repete, para servir de chave primária. UsaDate.now(): o número de milissegundos desde 1 de janeiro de 1970 (como cada milissegundo é diferente, o número nunca se repete).
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:
id: novoId()— cada cliente recebe a sua chave primária.criadoEm: new Date().toISOString().slice(0, 10)— a data de hoje como texto, no formatoAAAA-MM-DD(por exemplo2026-10-03). OtoISOString()dá algo como2026-10-03T09:15:00.000Ze oslice(0, 10)fica só com as 10 primeiras letras.guardar('clientes', clientes)— depois de acrescentar o cliente à lista, grava a lista inteira na gaveta.
⚠️ 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
- Grava todos os ficheiros e volta ao CRM no browser.
- Acrescenta 3 clientes diferentes.
- Carrega em F5.
- 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→ odados.jsnão foi gravado (Ctrl+S) ou colaste o bloco do passo 4 no ficheiro errado. Identifier 'clientes' has already been declared→ ficou olet 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 temguardar('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:5500ehttp://localhost:5500sã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:
- Carrega em F12 e abre o separador Aplicação (em inglês, Application). Se não o vires, clica em » para mostrar mais separadores.
- 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). - 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)
- No separador Aplicação, clica com o botão direito na chave
crm_clientese escolhe Eliminar (Delete). Depois carrega em F5. O que acontece à tabela? Porquê? - 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?
- Na Consola, escreve
JSON.stringify({ nome: 'Ana' })e depoisJSON.stringify(clientes). Reconheces o texto que viste no separador Aplicação? - Escreve
Date.now()na Consola várias vezes seguidas. O número muda? Porque é que serve para criar umiddiferente 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:
- Quantos registos tem a chave
crm_clientes? Como os contaste? - Que campos tem cada registo? Escreve os nomes de todos.
- Qual é o
iddo primeiro cliente? Quantos algarismos tem? - 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
- Faz o desafio ⭐.
- Na Consola, experimenta
JSON.stringify(clientes, null, 2). O que muda no texto? Para que achas que serve? - Na Consola, escreve
localStorage.lengthelocalStorage.key(0). O que te dizem?
🤖 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).