-
Hi It would be great if there were a way to get row data that is filtered only by column filters (without the global filters applied) and get a row model with only global filters applied. My use case is like this. I built a table using It would be great if we had I think this is a bit niche but if it is accepted I can work on the PR myself as I've already done it (albeit kind of ugly) for our use case. |
Beta Was this translation helpful? Give feedback.
Replies: 5 comments 3 replies
-
hey @MAST1999 did you ended-up finding a good solution for this? |
Beta Was this translation helpful? Give feedback.
-
It's actually pretty easy, i found it this way : |
Beta Was this translation helpful? Give feedback.
-
I actually found a workaround for this. Make sure you have global filters configuration working. You can just disable the const requiredData = useMemo(() => {
if (columnFilters.length) {
return data.filter((item) =>
columnFilters.every((filter) =>
(item[filter.id as keyof TData] as string).includes(
filter.value as string
)
)
)
}
return data
}, [columnFilters, data]) Then you can assign this to your data in the
by doing this it will work for global filter as required and for your text based column filters as well. Hope this helps. |
Beta Was this translation helpful? Give feedback.
-
how about doing calculations separately ?
|
Beta Was this translation helpful? Give feedback.
-
Hey everyone Just wanted to give an update since the latest table release we can do this much more elegantly using table features. Note that most of this code is copied from the internal implementation of Then you'll have access to import {
createRow,
getMemoOptions,
memo,
ResolvedColumnFilter,
Row,
RowData,
RowModel,
Table,
TableFeature,
} from '@tanstack/react-table';
export interface TablePreGlobalFilter<TData> {
getPreGlobalFiltering: () => RowModel<TData>;
}
export const TablePreGlobalFilteringFeature: TableFeature<any> = {
createTable: (table) => {
table.getPreGlobalFiltering = getPreGlobalFiltering()(table);
},
};
export function getPreGlobalFiltering<TData extends RowData>(): (
table: Table<TData>,
) => () => RowModel<TData> {
return (table) =>
memo(
() => [
table.getPreFilteredRowModel(),
table.getState().columnFilters,
table.getState().globalFilter,
],
(rowModel, columnFilters, globalFilter) => {
if (!rowModel.rows.length || (!columnFilters?.length && !globalFilter)) {
for (let i = 0; i < rowModel.flatRows.length; i++) {
rowModel.flatRows[i]!.columnFilters = {};
rowModel.flatRows[i]!.columnFiltersMeta = {};
}
return rowModel;
}
const resolvedColumnFilters: ResolvedColumnFilter<TData>[] = [];
const resolvedGlobalFilters: ResolvedColumnFilter<TData>[] = [];
(columnFilters ?? []).forEach((d) => {
const column = table.getColumn(d.id);
if (!column) {
return;
}
const filterFn = column.getFilterFn();
if (!filterFn) {
if (process.env.NODE_ENV !== 'production') {
console.warn(
`Could not find a valid 'column.filterFn' for column with the ID: ${column.id}.`,
);
}
return;
}
resolvedColumnFilters.push({
id: d.id,
filterFn,
resolvedValue: filterFn.resolveFilterValue?.(d.value) ?? d.value,
});
});
const filterableIds = (columnFilters ?? []).map((d) => d.id);
const globalFilterFn = table.getGlobalFilterFn();
const globallyFilterableColumns = table
.getAllLeafColumns()
.filter((column) => column.getCanGlobalFilter());
if (globalFilter && globalFilterFn && globallyFilterableColumns.length) {
filterableIds.push('__global__');
globallyFilterableColumns.forEach((column) => {
resolvedGlobalFilters.push({
id: column.id,
filterFn: globalFilterFn,
resolvedValue: globalFilterFn.resolveFilterValue?.(globalFilter) ?? globalFilter,
});
});
}
let currentColumnFilter;
// Flag the unfiltered row model with each filter state
for (let j = 0; j < rowModel.flatRows.length; j++) {
const row = rowModel.flatRows[j]!;
row.columnFilters = {};
if (resolvedColumnFilters.length) {
for (let i = 0; i < resolvedColumnFilters.length; i++) {
currentColumnFilter = resolvedColumnFilters[i]!;
const id = currentColumnFilter.id;
// Tag the row with the column filter state
row.columnFilters[id] = currentColumnFilter.filterFn(
row,
id,
currentColumnFilter.resolvedValue,
(filterMeta) => {
row.columnFiltersMeta[id] = filterMeta;
},
);
}
}
}
const filterRowsImpl = (row: Row<TData>) => {
// Horizontally filter rows through each column
for (let i = 0; i < filterableIds.length; i++) {
if (row.columnFilters[filterableIds[i]!] === false) {
return false;
}
}
return true;
};
// Filter final rows using all the active filters
return filterRows(rowModel.rows, filterRowsImpl, table);
},
getMemoOptions(table.options, 'debugTable', 'getFilteredRowModel', () =>
table._autoResetPageIndex(),
),
);
}
export function filterRows<TData extends RowData>(
rows: Row<TData>[],
filterRowImpl: (row: Row<TData>) => any,
table: Table<TData>,
) {
if (table.options.filterFromLeafRows) {
return filterRowModelFromLeafs(rows, filterRowImpl, table);
}
return filterRowModelFromRoot(rows, filterRowImpl, table);
}
function filterRowModelFromLeafs<TData extends RowData>(
rowsToFilter: Row<TData>[],
filterRow: (row: Row<TData>) => Row<TData>[],
table: Table<TData>,
): RowModel<TData> {
const newFilteredFlatRows: Row<TData>[] = [];
const newFilteredRowsById: Record<string, Row<TData>> = {};
const maxDepth = table.options.maxLeafRowFilterDepth ?? 100;
const recurseFilterRows = (rowsToFilter: Row<TData>[], depth = 0) => {
const rows: Row<TData>[] = [];
// Filter from children up first
for (let i = 0; i < rowsToFilter.length; i++) {
let row = rowsToFilter[i]!;
const newRow = createRow(
table,
row.id,
row.original,
row.index,
row.depth,
undefined,
row.parentId,
);
newRow.columnFilters = row.columnFilters;
if (row.subRows?.length && depth < maxDepth) {
newRow.subRows = recurseFilterRows(row.subRows, depth + 1);
row = newRow;
if (filterRow(row) && !newRow.subRows.length) {
rows.push(row);
newFilteredRowsById[row.id] = row;
newFilteredFlatRows.push(row);
continue;
}
if (filterRow(row) || newRow.subRows.length) {
rows.push(row);
newFilteredRowsById[row.id] = row;
newFilteredFlatRows.push(row);
continue;
}
} else {
row = newRow;
if (filterRow(row)) {
rows.push(row);
newFilteredRowsById[row.id] = row;
newFilteredFlatRows.push(row);
}
}
}
return rows;
};
return {
rows: recurseFilterRows(rowsToFilter),
flatRows: newFilteredFlatRows,
rowsById: newFilteredRowsById,
};
}
function filterRowModelFromRoot<TData extends RowData>(
rowsToFilter: Row<TData>[],
filterRow: (row: Row<TData>) => any,
table: Table<TData>,
): RowModel<TData> {
const newFilteredFlatRows: Row<TData>[] = [];
const newFilteredRowsById: Record<string, Row<TData>> = {};
const maxDepth = table.options.maxLeafRowFilterDepth ?? 100;
// Filters top level and nested rows
const recurseFilterRows = (rowsToFilter: Row<TData>[], depth = 0) => {
// Filter from parents downward first
const rows: Row<TData>[] = [];
// Apply the filter to any subRows
for (let i = 0; i < rowsToFilter.length; i++) {
let row = rowsToFilter[i]!;
const pass = filterRow(row);
if (pass) {
if (row.subRows?.length && depth < maxDepth) {
const newRow = createRow(
table,
row.id,
row.original,
row.index,
row.depth,
undefined,
row.parentId,
);
newRow.subRows = recurseFilterRows(row.subRows, depth + 1);
row = newRow;
}
rows.push(row);
newFilteredFlatRows.push(row);
newFilteredRowsById[row.id] = row;
}
}
return rows;
};
return {
rows: recurseFilterRows(rowsToFilter),
flatRows: newFilteredFlatRows,
rowsById: newFilteredRowsById,
};
} Then add this to a declare module '@tanstack/react-table' {
interface Table<TData extends RowData> extends TablePreGlobalFilter<TData> {}
} |
Beta Was this translation helpful? Give feedback.
Hey everyone
Just wanted to give an update since the latest table release we can do this much more elegantly using table features.
Note that most of this code is copied from the internal implementation of
getFilteredRowModel
.After defining this, you can pass it to the table like this:
_features: [TablePreGlobalFilteringFeature]
Then you'll have access to
getPreGlobalFiltering
on the table instance.