FreeGridv1.15.1한국어

Select editor and cascading lists

A drop-down of fixed choices with labels, disabled options and a placeholder; options from the row make dependent lists, and dependsOn clears a child value that no longer fits.

Live demoSelect and cascading lists

Country → city: the city list follows the country and is cleared when it no longer fits.

Code for this demo
import { createGrid, type ColumnDef, type Row } from '@freegridjs/core';
import { makeRows } from '../data';
import { button, separator, text, tr, type DemoContext } from '../ui';

const CITIES: Record<string, string[]> = { Korea: ['Seoul', 'Busan'], Japan: ['Tokyo', 'Osaka'], France: ['Paris', 'Lyon'] };
const DISTRICTS: Record<string, string[]> = { Seoul: ['Gangnam', 'Mapo', 'Jongno'], Busan: ['Haeundae', 'Suyeong'], Tokyo: ['Shibuya', 'Shinjuku'], Osaka: ['Namba', 'Umeda'], Paris: ['Le Marais', 'Montmartre'], Lyon: ['Presqu’île'] };
export default function mount(ctx: DemoContext) {
  const h = (en: string, ko: string) => tr(ctx.lang, { en, ko });
  const countries = Object.keys(CITIES);
  const data = makeRows(150).map((r, i) => {
    const country = countries[i % 3];
    const city = CITIES[country][i % 2] ?? CITIES[country][0];
    return { ...r, country, city, district: i % 4 ? DISTRICTS[city][0] : null, status: ['open', 'hold', 'open'][i % 3] };
  });
  const grid = createGrid(ctx.host, {
    columns: [
      { field: 'name', header: h('Name', '이름'), width: 140 },
      { field: 'status', header: h('Status', '상태'), width: 140, editable: true, editor: { type: 'select', options: [
        { value: 'open', label: h('Open', '진행') }, { value: 'hold', label: h('On hold', '보류') }, { value: 'closed', label: h('Closed (admin)', '종료(관리자)'), disabled: true }] } },
      { field: 'country', header: h('Country', '국가'), width: 120, editable: true, editor: { type: 'select', options: countries } },
      { field: 'city', header: h('City', '도시'), width: 120, editable: true, dependsOn: ['country'],
        editor: { type: 'select', placeholder: h('City…', '도시 선택'), options: (row: Row) => CITIES[row.country] ?? [] } },
      { field: 'district', header: h('District', '구'), width: 140, editable: true, dependsOn: ['city'],
        editor: { type: 'select', placeholder: h('District…', '구 선택'), options: (row: Row) => DISTRICTS[row.city] ?? [] } },
    ],
    data, locale: ctx.lang,
  });
  grid.on('cellValueChanged', e => ctx.log(`#${e.row.id} ${e.colId}: ${String(e.oldValue)} → ${String(e.newValue)}`));
  text(ctx, h('Change a country: a city that does not fit is cleared, and its district too. Ctrl+Z brings all back at once.',
    '국가를 바꿔 보세요. 맞지 않는 도시와 그 구가 비워지고, Ctrl+Z 한 번으로 모두 돌아옵니다.'));
  separator(ctx);
  button(ctx, h('Undo', '실행 취소'), () => grid.undo());
  return grid;
}

editor: { type: 'select', options } opens the browser's own drop-down with fixed choices. Options can be plain values or { value, label, disabled }; the cell shows the label and stores the value. For long lists use the combo editor; for a few choices shown at once, the radio editor.

Basic use

{ field: 'status', editable: true, editor: {
  type: 'select',
  placeholder: 'Choose…',                  // an empty first choice, shown for blank cells
  options: [
    { value: 'open', label: 'Open' },
    { value: 'hold', label: 'On hold' },
    { value: 'closed', label: 'Closed (admin only)', disabled: true },   // shown, cannot be chosen or pasted
  ],
} }

Options

NameTypeDescription
optionsSelectOption[] | (row) => SelectOption[]The choices. A function of the row makes a list that depends on other fields of the row (it is called each time the editor opens).
placeholderstringAdds an empty first choice with this text. A blank cell opens on it (instead of on the first option), and choosing it saves null.
dependsOn (column)string[]Fields this column's options come from. When one of them changes (edit, paste, fill), a value no longer in the options is cleared in the same undo step.

Cascading lists (country → city → district)

const CITIES = { KR: ['Seoul', 'Busan'], JP: ['Tokyo', 'Osaka'] };
const DISTRICTS = { Seoul: ['Gangnam', 'Mapo'], Busan: ['Haeundae'], Tokyo: ['Shibuya'], Osaka: ['Namba'] };

const columns = [
  { field: 'country', editable: true, editor: { type: 'select', options: Object.keys(CITIES) } },
  { field: 'city', editable: true, dependsOn: ['country'],
    editor: { type: 'select', placeholder: 'City…', options: row => CITIES[row.country] ?? [] } },
  // clearing a city clears its district too: the chain follows on
  { field: 'district', editable: true, dependsOn: ['city'],
    editor: { type: 'select', placeholder: 'District…', options: row => DISTRICTS[row.city] ?? [] } },
];
  • Changing the country to JP clears a Seoul city and its district; one Ctrl+Z brings all three back.
  • A value that still fits stays: changing KR → KR (or to a country that also has the city) keeps it.
  • Clearing follows paste and fill as well, so a pasted country column never leaves wrong cities behind.
  • Without dependsOn the list still follows the row, but old values are kept — use that when the child may stay as is.

Paste and filters

  • Pasting accepts a value or a label in any case; a value not in the options (or disabled) is skipped and counted.
  • A select column gets a list filter (set filter) that shows the option labels in option order, in the column menu and the filter row.

Questions about this page

How do I make a city list that depends on the country?

Give the city column options: row => CITIES[row.country] and dependsOn: ['country']. When the country changes, a city that is not in the new list is cleared in the same undo step.