I’m using Tom Select for one of my dropdowns in the form. The form has an option to dynamically add new rows to the form. The Tom Select works perfectly fine for the default row of the form, but it breaks when I add a new row by clicking on the button. additionally, it shows the error below.
Error populating dropdown: TypeError: Cannot read properties of
undefined (reading ‘trim’)
Here is my script. could you have any solution for this. I’m new to Javascript & Jquery.
$(document).ready(function () {
// Initialize Tom Select for the default product dropdown
initializeTomSelect('.product_id', true);
// Add new row functionality
$('#add_expense_row').click(function (e) {
e.preventDefault();
// Clone the row template
const newRow = $('#aod_line').clone();
newRow.removeAttr('id'); // Remove the ID to avoid duplication
newRow.find('input[type="number"], input[type="text"]').val(''); // Clear input values
newRow.find('.product_id').empty(); // Clear existing options
newRow.find('.product_id').append('<option value="" disabled selected>Select a product</option>'); // Add placeholder
newRow.find('#remove-line').prop('disabled', false); // Enable remove button
// Append the new row
$('#submit_div').before(newRow);
// Initialize Tom Select for the newly added dropdown after populating options
const productDropdown = newRow.find('.product_id');
initializeTomSelect(productDropdown, false);
});
// Remove row functionality
$(document).on('click', '#remove-line', function (e) {
e.preventDefault();
$(this).closest('.row').remove();
});
// Function to initialize Tom Select
function initializeTomSelect(selector, isStatic = false) {
$(selector).each(function () {
const dropdown = $(this);
// Populate options first before initializing Tom Select
populateProductDropdown(dropdown).then(() => {
console.log('Dropdown after population:', dropdown.html());
if (!dropdown[0].tomselect) { // Check if Tom Select is already initialized
new TomSelect(dropdown[0], {
placeholder: 'Select a product',
allowEmptyOption: true, // Allow empty dropdowns to prevent errors
});
}
}).catch((error) => {
console.error('Error populating dropdown:', error);
});
});
}
// Function to populate product dropdown with products
function populateProductDropdown(dropdown) {
return new Promise((resolve, reject) => {
$.ajax({
url: '../controller/operations_controller.php?status=get-products',
method: 'GET',
dataType: 'json',
success: function (data) {
const select = dropdown[0].tomselect;
if (select) {
// If Tom Select is already initialized
select.clearOptions(); // Clear existing options
select.addOption({ value: '', text: 'Select a product', disabled: true }); // Add placeholder
data.forEach((product) => {
select.addOption({ value: product.product_id, text: product.product_name });
});
select.refreshOptions(false); // Refresh without opening
} else {
// If Tom Select is not initialized yet
dropdown.empty();
dropdown.append('<option value="" disabled selected>Select a product</option>'); // Add placeholder
data.forEach((product) => {
dropdown.append(`<option value="${product.product_id}">${product.product_name}</option>`);
});
}
resolve(); // Resolve the promise after population
},
error: function (xhr, status, error) {
console.error('AJAX error:', xhr.responseText || error);
alert('Failed to load products. Please try again later.');
reject(error); // Reject the promise on error
}
});
});
}
});
Here are two screenshots of the first row and the second row which was added dynamically.
https://imgur.com/a/EbTM3Wa