AJAX Basic

Integrate with external APIs using AJAX/Fetch to load remote data dynamically. Full support for debounce, cache, custom templates and error handling.

REST Countries API

Real-time country search using the public REST Countries API.

View code

HTML

<select id="countries"></select>

JavaScript

const countriesSelect = new VanillaSmartSelect('#countries', {
  placeholder: 'Type to search countries...',
  allowClear: true,
  ajax: {
    url: 'https://restcountries.com/v3.1/name',
    delay: 300,
    transport: (params, config) => {
      const searchTerm = params.term || 'brazil';
      const url = `${config.url}/${encodeURIComponent(searchTerm)}`;
      
      const abortController = new AbortController();
      const fetchPromise = fetch(url, {
        signal: abortController.signal
      }).then(response => response.json());
      
      fetchPromise.abort = () => abortController.abort();
      return fetchPromise;
    },
    processResults: (data) => {
      const results = data.map(country => ({
        id: country.cca3,
        text: country.name.common,
        flag: country.flag,
        capital: country.capital?.[0],
        population: country.population
      }));
      return { results };
    }
  },
  templateResult: (item) => {
    return `
      <div style="display: flex; gap: 10px;">
        <span>${item.flag}</span>
        <div>
          <div>${item.text}</div>
          <small>Capital: ${item.capital}</small>
        </div>
      </div>
    `;
  }
});

ViaCEP - ZIP Code Search

Search for Brazilian addresses by ZIP code using the ViaCEP API.

View code

HTML

<select id="cep"></select>

JavaScript

const cepSelect = new VanillaSmartSelect('#cep', {
  placeholder: 'Type a ZIP code to search...',
  allowClear: true,
  ajax: {
    url: 'https://viacep.com.br/ws',
    delay: 400,
    transport: (params, config) => {
      const cep = (params.term || '').replace(/\D/g, '');
      if (cep.length < 8) return Promise.resolve([]);
      
      const url = `${config.url}/${cep}/json/`;
      return fetch(url)
        .then(response => response.json())
        .then(data => data.erro ? [] : [data]);
    },
    processResults: (data) => {
      const results = data.map(address => ({
        id: address.cep,
        text: `${address.cep} - ${address.logradouro}`,
        logradouro: address.logradouro,
        bairro: address.bairro,
        localidade: address.localidade,
        uf: address.uf
      }));
      return { results };
    }
  }
});

Movie Search (Simulated)

Search example with local data simulating a movies API.

View code

HTML

<select id="movies"></select>

JavaScript

const moviesSelect = new VanillaSmartSelect('#movies', {
  placeholder: 'Type to search movies...',
  allowClear: true,
  ajax: {
    delay: 300,
    transport: (params) => {
      return new Promise((resolve) => {
        setTimeout(() => {
          const term = (params.term || '').toLowerCase();
          const filtered = moviesDatabase.filter(movie =>
            movie.title.toLowerCase().includes(term)
          );
          resolve(filtered);
        }, 500);
      });
    },
    processResults: (data) => {
      return {
        results: data.map(movie => ({
          id: movie.id,
          text: movie.title,
          year: movie.year,
          genre: movie.genre,
          rating: movie.rating
        }))
      };
    }
  },
  templateResult: (item) => {
    return `
      <div style="display: flex; justify-content: space-between;">
        <div>
          <div>🎬 ${item.text}</div>
          <small>${item.year} • ${item.genre}</small>
        </div>
        <span style="background: #ffc107; padding: 4px 8px; border-radius: 4px;">
          ⭐ ${item.rating}
        </span>
      </div>
    `;
  }
});

Custom Headers

Configure custom HTTP headers, including Bearer authentication and Content-Type.

View code

HTML

<select id="auth-api"></select>

JavaScript

const authSelect = new VanillaSmartSelect('#auth-api', {
  placeholder: 'Search authenticated data...',
  ajax: {
    url: 'https://api.example.com/data',
    delay: 300,
    transport: (params, config) => {
      const abortController = new AbortController();

      const fetchPromise = fetch(config.url, {
        method: 'POST',
        headers: {
          'Authorization': 'Bearer YOUR_TOKEN_HERE',
          'Content-Type': 'application/json',
          'X-Custom-Header': 'CustomValue'
        },
        body: JSON.stringify({
          query: params.term,
          limit: 20
        }),
        signal: abortController.signal
      })
        .then(response => {
          if (!response.ok) throw new Error(`HTTP ${response.status}`);
          return response.json();
        });

      fetchPromise.abort = () => abortController.abort();
      return fetchPromise;
    },
    processResults: (data) => {
      return {
        results: data.results.map(item => ({
          id: item.id,
          text: item.name
        }))
      };
    }
  }
});

Error Handling

Monitor and handle AJAX request errors using custom events.

View code

HTML

<select id="error-demo"></select>

JavaScript

const errorSelect = new VanillaSmartSelect('#error-demo', {
  placeholder: 'Type to test...',
  ajax: {
    url: 'https://api.invalid-domain-example-12345.com/search',
    delay: 300,
    transport: (params, config) => {
      const abortController = new AbortController();

      const fetchPromise = fetch(config.url, {
        signal: abortController.signal
      })
        .then(response => {
          if (!response.ok) {
            throw new Error(`HTTP ${response.status}: ${response.statusText}`);
          }
          return response.json();
        });

      fetchPromise.abort = () => abortController.abort();
      return fetchPromise;
    }
  }
});

// Monitor events
const selectElement = document.getElementById('error-demo');

selectElement.addEventListener('vs:ajaxLoading', (e) => {
  console.log('🔄 Carregando...', e.detail);
});

selectElement.addEventListener('vs:ajaxSuccess', (e) => {
  console.log('✅ Sucesso!', e.detail.data);
  alert('Dados carregados com sucesso!');
});

selectElement.addEventListener('vs:ajaxError', (e) => {
  console.error('❌ Erro ao carregar:', e.detail.error);
  alert(`Erro: ${e.detail.error.message}`);
});

AJAX Events

Monitor request state using events:

JavaScript

// Available events
selectElement.addEventListener('vs:ajaxLoading', (e) => {
  console.log('Loading...', e.detail);
});

selectElement.addEventListener('vs:ajaxSuccess', (e) => {
  console.log('Success!', e.detail);
});

selectElement.addEventListener('vs:ajaxError', (e) => {
  console.error('Error:', e.detail);
});

Implemented Features

  • ✅ Asynchronous loading via fetch API
  • ✅ Automatic debounce to avoid excessive requests
  • ✅ Custom transport for full control of requests
  • ✅ Remote search with dynamic parameters
  • ✅ Loading indicator
  • ✅ Error handling with custom messages
  • ✅ Custom templates for results
  • ✅ AbortController support to cancel requests
  • ✅ Custom HTTP headers (Authorization, Content-Type)
  • ✅ Success and error events for monitoring