Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions src/lib/table/table-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1524,13 +1524,16 @@ export class TableUtils {

// If this is a FormFieldComponentDelegate then we can listen for when the value changes, otherwise we just render the custom delegate element
if (!!filterListener && delegate instanceof FormFieldComponentDelegate && isFunction(delegate.onChange)) {
if (!isDefined(columnConfig.filterDebounceTime) || isNumber(columnConfig.filterDebounceTime)) {
const debounceTime = isDefined(columnConfig.filterDebounceTime)
? (columnConfig.filterDebounceTime as number)
: TABLE_CONSTANTS.numbers.DEFAULT_FILTER_DEBOUNCE_TIME;
delegate.onChange(debounce((value: any) => filterListener(value, columnIndex), debounceTime));
} else {
delegate.onChange((value: any) => filterListener(value, columnIndex));
if (!(delegate as any).__forgeTableListenerAttached) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ankur-Thakur-NEU I'm not a fan of adding dynamic properties like this, especially ones that are boolean flags because things can get out of sync easily. Perhaps we could use a WeakMap and store listener references safely, and add some logic to the delegates to remove previous listeners when onChange() is called?

I'm thinking something like this at the class level:

// Track filter delegates and their associated listeners to prevent duplicates
private static _filterDelegateListeners = new WeakMap<IFormFieldComponentDelegate<any>, (value: any) => void>();

Then replace the logic here with the following:

    // If this is a FormFieldComponentDelegate then we can listen for when the value changes, otherwise we just render the custom delegate element
    if (!!filterListener && delegate instanceof FormFieldComponentDelegate && isFunction(delegate.onChange)) {
      // Check if we already have a listener for this delegate to prevent duplicates
      if (!TableUtils._filterDelegateListeners.has(delegate)) {
        let listener: (value: any) => void;
        
        if (!isDefined(columnConfig.filterDebounceTime) || isNumber(columnConfig.filterDebounceTime)) {
          const debounceTime = isDefined(columnConfig.filterDebounceTime)
            ? (columnConfig.filterDebounceTime as number)
            : TABLE_CONSTANTS.numbers.DEFAULT_FILTER_DEBOUNCE_TIME;
          listener = debounce((value: any) => filterListener(value, columnIndex), debounceTime);
        } else {
          listener = (value: any) => filterListener(value, columnIndex);
        }
        
        // Store the listener in the WeakMap and attach it
        TableUtils._filterDelegateListeners.set(delegate, listener);
        delegate.onChange(listener);
      }
    }

This is untested but it should safely store the delegate instances and listeners so they can be cleaned up by GC, and hopefully net the same result. Could you try this or something similar and let me know? We should also add a test for this as well.

if (!isDefined(columnConfig.filterDebounceTime) || isNumber(columnConfig.filterDebounceTime)) {
const debounceTime = isDefined(columnConfig.filterDebounceTime)
? (columnConfig.filterDebounceTime as number)
: TABLE_CONSTANTS.numbers.DEFAULT_FILTER_DEBOUNCE_TIME;
delegate.onChange(debounce((value: any) => filterListener(value, columnIndex), debounceTime));
} else {
delegate.onChange((value: any) => filterListener(value, columnIndex));
}
(delegate as any).__forgeTableListenerAttached = true;
}
}

Expand Down