Comprehensive guide for managing choices, selected items, and values in Choices.js with full programmatic control over data operations.
{ .api }
interface Choice {
id: number;
label: string;
value: string;
selected: boolean;
disabled: boolean;
customProperties?: Record<string, any>;
}
interface Item extends Choice {
choiceId: number;
highlighted: boolean;
}
interface Group {
id: number;
value: string;
active: boolean;
disabled: boolean;
choices?: Choice[];
}{ .api }
/**
* Set the available choices for selection
* @param choices - Array of choice objects or groups
* @param value - Property name for value (default: 'value')
* @param label - Property name for label (default: 'label')
* @param replaceChoices - Replace existing choices (default: false)
* @returns {Choices} - Returns instance for chaining
*/
setChoices(
choices: (Choice | Group)[],
value?: string,
label?: string,
replaceChoices?: boolean
): ChoicesBasic Usage:
// Simple choices array
choices.setChoices([
{ value: 'option1', label: 'Option 1' },
{ value: 'option2', label: 'Option 2', selected: true },
{ value: 'option3', label: 'Option 3', disabled: true }
]);
// Replace all existing choices
choices.setChoices(newChoices, 'value', 'label', true);Custom Property Mapping:
// Custom property names
const apiData = [
{ id: 1, name: 'First Choice', code: 'FIRST' },
{ id: 2, name: 'Second Choice', code: 'SECOND' }
];
choices.setChoices(apiData, 'code', 'name');
// Maps 'code' to value, 'name' to labelWith Groups:
const groupedChoices = [
{
label: 'Fruits',
id: 1,
disabled: false,
choices: [
{ value: 'apple', label: 'Apple' },
{ value: 'banana', label: 'Banana' }
]
},
{
label: 'Vegetables',
id: 2,
disabled: false,
choices: [
{ value: 'carrot', label: 'Carrot' },
{ value: 'lettuce', label: 'Lettuce' }
]
}
];
choices.setChoices(groupedChoices);With Custom Properties:
const enrichedChoices = [
{
value: 'premium',
label: 'Premium Plan',
customProperties: {
price: 29.99,
features: ['Advanced Analytics', 'Priority Support'],
badge: 'Most Popular'
}
},
{
value: 'basic',
label: 'Basic Plan',
customProperties: {
price: 9.99,
features: ['Basic Analytics'],
badge: null
}
}
];
choices.setChoices(enrichedChoices);
// Access custom properties in templates or events
choices.passedElement.element.addEventListener('addItem', (event) => {
const customProps = event.detail.customProperties;
console.log('Selected plan price:', customProps.price);
});{ .api }
/**
* Remove all available choices from dropdown
* @returns {Choices} - Returns instance for chaining
*/
clearChoices(): ChoicesUsage:
// Remove all choices but keep selected items
choices.clearChoices();
// Typically followed by new choices
choices.clearChoices().setChoices(newChoicesArray);// Async loading pattern
async function loadChoices(searchTerm) {
choices.clearChoices();
const response = await fetch(`/api/choices?search=${searchTerm}`);
const data = await response.json();
choices.setChoices(data.results, 'id', 'name', true);
}
// Debounced search
let searchTimeout;
choices.input.element.addEventListener('input', (event) => {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(() => {
loadChoices(event.target.value);
}, 300);
});{ .api }
/**
* Set the selected value(s) programmatically
* @param items - Values to select (string, array, or item objects)
* @returns {Choices} - Returns instance for chaining
*/
setValue(items: string[] | string | Item[] | Item): ChoicesString Values:
// Single value
choices.setValue('option1');
// Multiple values
choices.setValue(['option1', 'option2', 'option3']);
// Empty array clears selection
choices.setValue([]);Item Objects:
// Single item object
choices.setValue({
value: 'custom',
label: 'Custom Option',
customProperties: { source: 'manual' }
});
// Multiple item objects
choices.setValue([
{ value: 'item1', label: 'Item 1' },
{ value: 'item2', label: 'Item 2' }
]);{ .api }
/**
* Get current values or items
* @param valueOnly - Return only values (true) or full items (false)
* @returns {string | string[] | Item | Item[]} Current selection
*/
getValue(valueOnly?: boolean): string | string[] | Item | Item[]Usage:
// Get values only (default)
const values = choices.getValue(); // ['value1', 'value2']
const values = choices.getValue(true); // ['value1', 'value2']
// Get full item objects
const items = choices.getValue(false);
// Returns: [
// { id: 1, value: 'value1', label: 'Label 1', selected: true, choiceId: 10 },
// { id: 2, value: 'value2', label: 'Label 2', selected: true, choiceId: 11 }
// ]
// For single select, returns single value/item (not array)
const singleValue = singleSelectChoices.getValue(); // 'selected-value'
const singleItem = singleSelectChoices.getValue(false); // { id: 1, value: '...', ... }{ .api }
/**
* Select an existing choice by its value
* @param value - The value to select
* @returns {Choices} - Returns instance for chaining
*/
setChoiceByValue(value: string): ChoicesUsage:
// Select from existing choices
choices.setChoiceByValue('existing-option');
// Works with multi-select (adds to selection)
multiChoices.setChoiceByValue('additional-option');
// No effect if value doesn't exist in choices
choices.setChoiceByValue('nonexistent-value');{ .api }
/**
* Remove selected items by their values
* @param value - Value to remove
* @returns {Choices} - Returns instance for chaining
*/
removeActiveItemsByValue(value: string): ChoicesUsage:
// Remove specific item
choices.removeActiveItemsByValue('unwanted-option');
// Remove multiple (call multiple times)
['item1', 'item2'].forEach(value => {
choices.removeActiveItemsByValue(value);
});{ .api }
/**
* Remove active items, optionally excluding one
* @param excludedId - ID of item to keep (optional)
* @returns {Choices} - Returns instance for chaining
*/
removeActiveItems(excludedId?: number): ChoicesUsage:
// Remove all selected items
choices.removeActiveItems();
// Remove all except specific item
const keepItem = choices.getValue(false)[0]; // Get first item
choices.removeActiveItems(keepItem.id);{ .api }
/**
* Remove currently highlighted items
* @param runEvent - Whether to trigger removeItem event
* @returns {Choices} - Returns instance for chaining
*/
removeHighlightedItems(runEvent?: boolean): ChoicesUsage:
// Remove highlighted items (typically from keyboard interaction)
choices.removeHighlightedItems();
// Remove without triggering events
choices.removeHighlightedItems(false);{ .api }
/**
* Highlight an item (visual selection state)
* @param item - Item to highlight
* @param runEvent - Whether to trigger highlightItem event
* @returns {Choices} - Returns instance for chaining
*/
highlightItem(item: Item, runEvent?: boolean): Choices{ .api }
/**
* Remove highlight from an item
* @param item - Item to unhighlight
* @returns {Choices} - Returns instance for chaining
*/
unhighlightItem(item: Item): Choices{ .api }
/**
* Highlight all selected items
* @returns {Choices} - Returns instance for chaining
*/
highlightAll(): Choices
/**
* Remove highlights from all items
* @returns {Choices} - Returns instance for chaining
*/
unhighlightAll(): ChoicesUsage:
// Get items for highlighting
const items = choices.getValue(false);
// Highlight specific item
choices.highlightItem(items[0]);
// Bulk operations
choices.highlightAll(); // Highlight everything
choices.unhighlightAll(); // Clear all highlights
// Keyboard-style selection
choices.unhighlightAll();
choices.highlightItem(items[2]); // Highlight only third item// Sync with hidden form input
const hiddenInput = document.getElementById('hidden-values');
choices.passedElement.element.addEventListener('change', (event) => {
hiddenInput.value = JSON.stringify(event.detail.value);
});
// Initialize from hidden input
if (hiddenInput.value) {
const savedValues = JSON.parse(hiddenInput.value);
choices.setValue(savedValues);
}// Redux/Vuex pattern
function syncChoicesWithStore(choices, store) {
// Update store when choices change
choices.passedElement.element.addEventListener('change', (event) => {
store.dispatch('updateSelection', event.detail.value);
});
// Update choices when store changes
store.subscribe(() => {
const currentSelection = store.getState().selection;
choices.setValue(currentSelection);
});
}// Auto-save changes
choices.passedElement.element.addEventListener('addItem', async (event) => {
await fetch('/api/user/preferences', {
method: 'POST',
body: JSON.stringify({
selectedItems: choices.getValue()
})
});
});
// Load from API
async function initializeFromAPI(choices) {
const [choicesData, selectedData] = await Promise.all([
fetch('/api/choices').then(r => r.json()),
fetch('/api/user/selected').then(r => r.json())
]);
choices.setChoices(choicesData);
choices.setValue(selectedData);
}// Avoid multiple DOM updates
choices.clearStore(); // Clear everything at once
// Set everything in one operation
choices.setChoices(allChoices, 'value', 'label', true);
choices.setValue(selectedValues);
// Chain operations to minimize renders
choices
.clearChoices()
.setChoices(newChoices)
.setValue(newSelection);// Transform API data to Choices format
function transformAPIData(apiResponse) {
return apiResponse.items.map(item => ({
value: item.id,
label: item.displayName,
disabled: !item.active,
customProperties: {
category: item.category,
metadata: item.metadata
}
}));
}
// Apply transformation
const transformedData = transformAPIData(apiData);
choices.setChoices(transformedData, 'value', 'label', true);// Custom filtering
function filterChoicesByCategory(choices, category) {
const filtered = choices.filter(choice =>
choice.customProperties?.category === category
);
return filtered;
}
// Apply filter
const categoryChoices = filterChoicesByCategory(allChoices, 'premium');
choices.setChoices(categoryChoices, 'value', 'label', true);
// Search-based filtering
function searchChoices(choices, searchTerm) {
return choices.filter(choice =>
choice.label.toLowerCase().includes(searchTerm.toLowerCase()) ||
choice.value.toLowerCase().includes(searchTerm.toLowerCase())
);
}// Validate before setting
function setValidatedChoices(choices, newChoices) {
const valid = newChoices.every(choice =>
choice.value && choice.label &&
typeof choice.value === 'string'
);
if (!valid) {
console.error('Invalid choice data');
return;
}
choices.setChoices(newChoices);
}
// Handle missing values gracefully
function safeSetValue(choices, values) {
const availableValues = choices.getValue().map(item => item.value);
const validValues = values.filter(value =>
availableValues.includes(value)
);
choices.setValue(validValues);
}This comprehensive data management guide covers all operations for effectively controlling Choices.js data state and synchronization.