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