Documentação

Formulário de Endereço com Selects em Cascata

Crie formulários de endereço profissionais com selects em cascata de país, estado e cidade

Aprenda a criar um formulário de endereço completo com dropdowns país → estado → cidade usando os endpoints select da CountryDataAPI.

Visão Geral

Este guia mostra como criar inputs select em cascata para:

  • Seleção de país - Carregar todos os países disponíveis
  • Seleção de estado/província - Filtrado pelo país selecionado
  • Seleção de cidade - Filtrado pelo estado selecionado

Os endpoints select são otimizados para formulários, retornando apenas os campos id e name para tamanho mínimo de payload e máximo desempenho.

Pré-requisitos

  • Uma chave da CountryDataAPI (Obtenha uma aqui)
  • Conhecimento básico de JavaScript e HTML
  • Navegador moderno com suporte à API fetch

Passo 1: Carregar Países

Primeiro, carregue todos os países quando sua página carregar. Os países raramente mudam, então você pode armazenar esses dados em cache no localStorage para melhor desempenho.

const API_KEY = 'sua-chave-api';
const BASE_URL = 'https://api.countrydataapi.com/v1';

async function loadCountries() {
  try {
    const response = await fetch(
      `${BASE_URL}/select/countries?apikey=${API_KEY}&lang=pt`
    );
    const { success, data, error } = await response.json();

    if (!success) {
      console.error('Erro da API:', error);
      return;
    }

    const countrySelect = document.getElementById('country');
    data.forEach(country => {
      const option = new Option(country.name, country.id);
      countrySelect.add(option);
    });
  } catch (err) {
    console.error('Erro de Rede:', err);
  }
}

// Chamar ao carregar a página
document.addEventListener('DOMContentLoaded', loadCountries);

Dica: Armazene a lista de países em cache no localStorage para evitar chamadas repetidas à API. Os países não mudam frequentemente!

Passo 2: Carregar Estados Quando o País Mudar

Quando um usuário selecionar um país, carregue os estados/províncias correspondentes e redefina o dropdown de cidades.

document.getElementById('country').addEventListener('change', async (e) => {
  const countryId = e.target.value;
  const stateSelect = document.getElementById('state');
  const citySelect = document.getElementById('city');

  // Redefinir dropdowns de estado e cidade
  stateSelect.innerHTML = '<option value="">Selecione o estado...</option>';
  citySelect.innerHTML = '<option value="">Selecione a cidade...</option>';
  stateSelect.disabled = true;
  citySelect.disabled = true;

  if (!countryId) return;

  try {
    const response = await fetch(
      `${BASE_URL}/select/states?apikey=${API_KEY}&country=${countryId}&lang=pt`
    );
    const { success, data, error } = await response.json();

    if (!success) {
      console.error('Erro da API:', error);
      return;
    }

    data.forEach(state => {
      stateSelect.add(new Option(state.name, state.id));
    });

    stateSelect.disabled = false;
  } catch (err) {
    console.error('Erro de Rede:', err);
  }
});

Passo 3: Carregar Cidades Quando o Estado Mudar

Por fim, carregue as cidades quando um estado for selecionado.

document.getElementById('state').addEventListener('change', async (e) => {
  const stateId = e.target.value;
  const citySelect = document.getElementById('city');

  // Redefinir dropdown de cidade
  citySelect.innerHTML = '<option value="">Selecione a cidade...</option>';
  citySelect.disabled = true;

  if (!stateId) return;

  try {
    const response = await fetch(
      `${BASE_URL}/select/cities?apikey=${API_KEY}&state=${stateId}&lang=pt`
    );
    const { success, data, error } = await response.json();

    if (!success) {
      console.error('Erro da API:', error);
      return;
    }

    data.forEach(city => {
      citySelect.add(new Option(city.name, city.id));
    });

    citySelect.disabled = false;
  } catch (err) {
    console.error('Erro de Rede:', err);
  }
});

Exemplo HTML Completo

<!DOCTYPE html>
<html lang="pt">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Formulário de Endereço - CountryDataAPI</title>
  <style>
    body {
      font-family: system-ui, -apple-system, sans-serif;
      max-width: 600px;
      margin: 50px auto;
      padding: 20px;
    }
    .form-group {
      margin-bottom: 20px;
    }
    label {
      display: block;
      margin-bottom: 8px;
      font-weight: 600;
      color: #333;
    }
    select {
      width: 100%;
      padding: 10px;
      border: 2px solid #e2e8f0;
      border-radius: 6px;
      font-size: 16px;
      background-color: white;
      cursor: pointer;
    }
    select:disabled {
      background-color: #f7fafc;
      cursor: not-allowed;
      opacity: 0.6;
    }
    select:focus {
      outline: none;
      border-color: #3b82f6;
    }
    button {
      background-color: #3b82f6;
      color: white;
      padding: 12px 24px;
      border: none;
      border-radius: 6px;
      font-size: 16px;
      font-weight: 600;
      cursor: pointer;
      width: 100%;
    }
    button:disabled {
      background-color: #cbd5e0;
      cursor: not-allowed;
    }
  </style>
</head>
<body>
  <h1>Formulário de Endereço</h1>

  <form id="address-form">
    <div class="form-group">
      <label for="country">País *</label>
      <select id="country" name="country" required>
        <option value="">Selecione o país...</option>
      </select>
    </div>

    <div class="form-group">
      <label for="state">Estado *</label>
      <select id="state" name="state" required disabled>
        <option value="">Selecione o estado...</option>
      </select>
    </div>

    <div class="form-group">
      <label for="city">Cidade *</label>
      <select id="city" name="city" required disabled>
        <option value="">Selecione a cidade...</option>
      </select>
    </div>

    <div class="form-group">
      <label for="street">Endereço *</label>
      <input type="text" id="street" name="street" required
             style="width: 100%; padding: 10px; border: 2px solid #e2e8f0; border-radius: 6px;">
    </div>

    <button type="submit">Enviar Endereço</button>
  </form>

  <script src="address-form.js"></script>
</body>
</html>

Envio do Formulário

Trate o envio do formulário para obter os valores selecionados:

document.getElementById('address-form').addEventListener('submit', (e) => {
  e.preventDefault();

  const formData = new FormData(e.target);
  const address = {
    country: formData.get('country'),
    state: formData.get('state'),
    city: formData.get('city'),
    street: formData.get('street')
  };

  console.log('Endereço enviado:', address);

  // Envie para seu backend ou processe conforme necessário
  // fetch('/api/save-address', {
  //   method: 'POST',
  //   headers: { 'Content-Type': 'application/json' },
  //   body: JSON.stringify(address)
  // });
});

Uso de Tokens

Esta implementação é altamente eficiente no uso de tokens:

  • 1 token para carregar países (uma vez por carregamento de página, pode ser armazenado em cache)
  • 1 token por consulta de estados (quando o usuário seleciona um país)
  • 1 token por consulta de cidades (quando o usuário seleciona um estado)

Exemplo: Um usuário preenchendo o formulário completamente usa apenas 3 tokens no total.

Dicas de Otimização

1. Armazenar Dados de Países em Cache

Os países não mudam frequentemente. Armazene-os em cache no localStorage:

async function loadCountries() {
  const cached = localStorage.getItem('countries');

  if (cached) {
    const data = JSON.parse(cached);
    populateCountrySelect(data);
    return;
  }

  const response = await fetch(
    `${BASE_URL}/select/countries?apikey=${API_KEY}&lang=pt`
  );
  const { success, data } = await response.json();

  if (success) {
    localStorage.setItem('countries', JSON.stringify(data));
    populateCountrySelect(data);
  }
}

function populateCountrySelect(countries) {
  const select = document.getElementById('country');
  countries.forEach(country => {
    select.add(new Option(country.name, country.id));
  });
}

2. Adicionar Estados de Carregamento

Mostre feedback visual enquanto os dados carregam:

async function loadStates(countryId) {
  const stateSelect = document.getElementById('state');
  stateSelect.innerHTML = '<option>Carregando...</option>';
  stateSelect.disabled = true;

  // ... buscar estados

  stateSelect.disabled = false;
}

3. Debounce na Entrada de Pesquisa

Se você adicionar um dropdown pesquisável, use debounce na entrada:

let debounceTimer;
searchInput.addEventListener('input', (e) => {
  clearTimeout(debounceTimer);
  debounceTimer = setTimeout(() => {
    filterOptions(e.target.value);
  }, 300);
});

4. Tratar Erros com Elegância

Sempre forneça mensagens de erro amigáveis:

try {
  // Chamada da API
} catch (err) {
  const select = document.getElementById('state');
  select.innerHTML = '<option>Erro ao carregar estados. Tente novamente.</option>';
}

Suporte Multi-idioma

A API suporta múltiplos idiomas. Altere o parâmetro lang:

// Espanhol
`${BASE_URL}/select/countries?apikey=${API_KEY}&lang=es`

// Português
`${BASE_URL}/select/countries?apikey=${API_KEY}&lang=pt`

// Francês
`${BASE_URL}/select/countries?apikey=${API_KEY}&lang=fr`

// Alemão
`${BASE_URL}/select/countries?apikey=${API_KEY}&lang=de`

// Italiano
`${BASE_URL}/select/countries?apikey=${API_KEY}&lang=it`

Próximos Passos

Precisa de Ajuda?

Se você tiver dúvidas ou precisar de assistência:


Dica Profissional: Para aplicações em produção, considere implementar um componente dropdown personalizado com funcionalidade de pesquisa para lidar com países com muitos estados/cidades. Bibliotecas como Select2, Choices.js ou autocomplete personalizado podem melhorar significativamente a experiência do usuário.