Skip to content

Selection Cascade Filter

Use filter.selection.cascadeOptions.enabled to make selection options context-aware.

In cascade mode, RevoGrid builds each column’s selection list from rows matching all active filters except filters for the current column. This keeps the current column reversible while still narrowing options in related columns.

Source code
TypeScriptts
grid.plugins = [AdvanceFilterPlugin];
grid.columns = [
  { prop: 'country', name: 'Country', filter: [FIlTER_SELECTION] },
  { prop: 'city', name: 'City', filter: [FIlTER_SELECTION] },
];
grid.filter = {
  selection: {
    cascadeOptions: {
      enabled: true,
      optionVisibility: 'hide',
    },
  },
};

return [
  { country: 'Germany', city: 'Berlin' },
  { country: 'Germany', city: 'Munich' },
  { country: 'France', city: 'Paris' },
  { country: 'France', city: 'Lyon' },
  { country: 'Poland', city: 'Warsaw' },
  { country: 'Poland', city: 'Krakow' },
];

Use optionVisibility to control how values outside that context appear. A value is context-valid when at least one source row containing that value matches the active filters in the other columns.

ModeOptions in the popupContext-invalid optionsBest suited for
hideContext-valid values onlyNot renderedCompact, spreadsheet-style cascading filters
disableComplete column domainVisible but disabledShowing users why a known value is currently unavailable
showComplete column domainVisible and interactiveWorkflows where users must edit future filter state across the complete domain

hide is the default when cascading is enabled. The modes change option presentation and interaction only; they do not change how rows are filtered.

cascadeOptions: {
enabled: true,
optionVisibility: 'hide',
}

Use hide for the most direct cascading experience. If the Country filter leaves only Germany, the City popup contains only Berlin and Munich. Paris, Lyon, Warsaw, and Krakow are omitted because they cannot affect the rows in the current country context.

The current column’s own filter is still ignored while its option list is built. For example, opening Country continues to show every country allowed by the other active columns, so users can reverse or broaden the Country selection.

cascadeOptions: {
enabled: true,
optionVisibility: 'disable',
}

Use disable when users benefit from seeing the complete domain. With Germany active, Berlin and Munich remain interactive while cities from France and Poland remain visible with disabled checkboxes.

  • Disabled options remain searchable and follow the configured sort order.
  • Clicking a disabled option does not change the filter model.
  • Select All and tree-selection cascades operate only on context-valid options.
  • A custom itemTemplate receives disabled: true for unavailable options.

This mode is useful when an unavailable value should be discoverable without allowing it to change the current cascade.

show: keep the complete domain interactive

Section titled “show: keep the complete domain interactive”
cascadeOptions: {
enabled: true,
optionVisibility: 'show',
}

Use show when users must manage selections across the complete domain even when some values cannot currently affect visible rows. With Germany active, every city remains visible and interactive.

Changing Paris while Germany is active updates the stored City exclusion, but it does not immediately change the displayed rows because no German row has City = Paris. The change becomes observable if the Country filter is later broadened to include France. This makes show useful for preparing filter state ahead of an upstream change, but it is less compact than hide and offers less availability guidance than disable.

Selection filters store excluded values. Every visibility mode preserves those exclusions when an option becomes context-invalid; cascading never silently prunes them.

For example, if Paris is excluded and Country is narrowed to Germany:

  • hide temporarily removes Paris from the popup.
  • disable keeps Paris visible and disabled.
  • show keeps Paris visible and interactive.

When France becomes valid again, Paris returns with its previous unchecked state. The FilterHeader count and tooltip always summarize context-valid values, independently of popup mode. Consequently, disable and show can display more popup options than the denominator shown in the header badge.

Columns with cellParser build and compare their cascading options from parsed values, consistent with the Core filter parsed values behavior.

Set filter.selection.cascadeOptions.showDependencyNumbers to true to show 1, 2, and later badges on active header filter icons in the order those filters are applied. The badges are hidden by default; omit this option or set it to false to keep them hidden.

Source code
TypeScriptts
import { defineCustomElements } from '@revolist/revogrid/loader';
import { AdvanceFilterPlugin, ColumnStretchPlugin } from '@revolist/revogrid-pro';
import { currentTheme } from '../composables/useRandomData';

defineCustomElements();

const rows = [
  { country: 'Germany', city: 'Berlin', department: 'Sales' },
  { country: 'Germany', city: 'Munich', department: 'Engineering' },
  { country: 'France', city: 'Paris', department: 'Sales' },
  { country: 'France', city: 'Lyon', department: 'Support' },
  { country: 'Poland', city: 'Warsaw', department: 'Engineering' },
  { country: 'Poland', city: 'Krakow', department: 'Support' },
];

export function load(parentSelector: string) {
  const grid = document.createElement('revo-grid');

  grid.columns = [
    { name: 'Country', prop: 'country', filter: ['selection'] },
    { name: 'City', prop: 'city', filter: ['selection'] },
    { name: 'Department', prop: 'department', filter: ['selection'] },
  ];

  grid.stretch = 'last';
  grid.plugins = [AdvanceFilterPlugin, ColumnStretchPlugin];
  grid.filter = {
    multiFilterItems: {
      department: [{ id: 0, type: 'selection', value: ['sales', 'support'], relation: 'and' }],
      country: [{ id: 1, type: 'selection', value: ['france', 'poland'], relation: 'and' }],
    },
    selection: {
      cascadeOptions: {
        enabled: true,
        optionVisibility: 'hide',
        showDependencyNumbers: true,
      },
    },
  };
  grid.resize = true;
  grid.theme = currentTheme().isDark() ? 'darkCompact' : 'compact';
  grid.hideAttribution = true;

  document.querySelector(parentSelector)?.appendChild(grid);
  grid.source = rows;

  return () => grid.remove();
}
Vuevue
<template>
  <RevoGrid
    :theme="isDark ? 'darkCompact' : 'compact'"
    :columns="columns"
    :source="rows"
    :plugins="plugins"
    stretch="last"
    :filter="filter"
    hide-attribution
  />
</template>

<script setup lang="ts">
import { computed, ref } from 'vue';
import RevoGrid from '@revolist/vue3-datagrid';
import { AdvanceFilterPlugin, ColumnStretchPlugin } from '@revolist/revogrid-pro';
import { currentThemeVue } from '../composables/useRandomData';

const { isDark } = currentThemeVue();

const columns = ref([
  { name: 'Country', prop: 'country', filter: ['selection'] },
  { name: 'City', prop: 'city', filter: ['selection'] },
  { name: 'Department', prop: 'department', filter: ['selection'] },
]);

const rows = ref([
  { country: 'Germany', city: 'Berlin', department: 'Sales' },
  { country: 'Germany', city: 'Munich', department: 'Engineering' },
  { country: 'France', city: 'Paris', department: 'Sales' },
  { country: 'France', city: 'Lyon', department: 'Support' },
  { country: 'Poland', city: 'Warsaw', department: 'Engineering' },
  { country: 'Poland', city: 'Krakow', department: 'Support' },
]);

const plugins = [AdvanceFilterPlugin, ColumnStretchPlugin];


const filter = computed(() => ({
  multiFilterItems: {
    department: [{ id: 0, type: 'selection', value: ['sales', 'support'], relation: 'and' }],
    country: [{ id: 1, type: 'selection', value: ['france', 'poland'], relation: 'and' }],
  },
  selection: {
    cascadeOptions: {
      enabled: true,
      optionVisibility: 'hide' as const,
      showDependencyNumbers: true,
    },
  },
}));
</script>
Reacttsx
import { useMemo } from 'react';
import { RevoGrid } from '@revolist/react-datagrid';
import { AdvanceFilterPlugin, ColumnStretchPlugin } from '@revolist/revogrid-pro';
import { currentTheme } from '../composables/useRandomData';

const rows = [
  { country: 'Germany', city: 'Berlin', department: 'Sales' },
  { country: 'Germany', city: 'Munich', department: 'Engineering' },
  { country: 'France', city: 'Paris', department: 'Sales' },
  { country: 'France', city: 'Lyon', department: 'Support' },
  { country: 'Poland', city: 'Warsaw', department: 'Engineering' },
  { country: 'Poland', city: 'Krakow', department: 'Support' },
];

export default function FilterSelectionCascade() {
  const { isDark } = currentTheme();

  const columns = useMemo(
    () => [
      { name: 'Country', prop: 'country', filter: ['selection'] },
      { name: 'City', prop: 'city', filter: ['selection'] },
      { name: 'Department', prop: 'department', filter: ['selection'] },
    ],
    [],
  );

  const plugins = useMemo(() => [AdvanceFilterPlugin, ColumnStretchPlugin], []);

  const filter = useMemo(
    () => ({
      multiFilterItems: {
        department: [{ id: 0, type: 'selection', value: ['sales', 'support'], relation: 'and' }],
        country: [{ id: 1, type: 'selection', value: ['france', 'poland'], relation: 'and' }],
      },
      selection: {
        cascadeOptions: {
          enabled: true,
          optionVisibility: 'hide' as const,
          showDependencyNumbers: true,
        },
      },
    }),
    [],
  );

  return (
    <RevoGrid
      columns={columns}
      source={rows}
      plugins={plugins}
      stretch="last"
      filter={filter}
      hide-attribution
      theme={isDark() ? 'darkCompact' : 'compact'}
    />
  );
}
Angularts
import { Component, ViewEncapsulation } from '@angular/core';
import { RevoGrid } from '@revolist/angular-datagrid';
import { AdvanceFilterPlugin, ColumnStretchPlugin } from '@revolist/revogrid-pro';
import { currentTheme } from '../composables/useRandomData';

@Component({
  selector: 'filter-selection-cascade-grid',
  standalone: true,
  imports: [RevoGrid],
  template: `
    <revo-grid
      [plugins]="plugins"
      [columns]="columns"
      [source]="source"
      stretch="last"
      [filter]="filter"
      [hideAttribution]="true"
      [theme]="theme"
      style="min-height: 400px;"
    ></revo-grid>
  `,
  encapsulation: ViewEncapsulation.None,
})
export class FilterSelectionCascadeGridComponent {
  theme = currentTheme().isDark() ? 'darkCompact' : 'compact';

  source = [
    { country: 'Germany', city: 'Berlin', department: 'Sales' },
    { country: 'Germany', city: 'Munich', department: 'Engineering' },
    { country: 'France', city: 'Paris', department: 'Sales' },
    { country: 'France', city: 'Lyon', department: 'Support' },
    { country: 'Poland', city: 'Warsaw', department: 'Engineering' },
    { country: 'Poland', city: 'Krakow', department: 'Support' },
  ];

  columns = [
    { name: 'Country', prop: 'country', filter: ['selection'] },
    { name: 'City', prop: 'city', filter: ['selection'] },
    { name: 'Department', prop: 'department', filter: ['selection'] },
  ];

  filter = {
    multiFilterItems: {
      department: [{ id: 0, type: 'selection', value: ['sales', 'support'], relation: 'and' }],
      country: [{ id: 1, type: 'selection', value: ['france', 'poland'], relation: 'and' }],
    },
    selection: {
      cascadeOptions: {
        enabled: true,
        optionVisibility: 'hide' as const,
        showDependencyNumbers: true,
      },
    },
  };

  plugins = [AdvanceFilterPlugin, ColumnStretchPlugin];
}
grid.filter = {
multiFilterItems: {
department: [{ id: 0, type: 'selection', value: ['sales', 'support'], relation: 'and' }],
country: [{ id: 1, type: 'selection', value: ['france', 'poland'], relation: 'and' }],
},
selection: {
cascadeOptions: {
enabled: true,
optionVisibility: 'hide',
showDependencyNumbers: true,
},
},
};

If selection.getItems is provided, that custom source is used as-is. Cascade visibility modes are only applied to the built-in source loader; RevoGrid does not infer contextual availability for custom items.