플러그인 설명 / Master / Detail개발 매뉴얼로 돌아가기

Master / Detail 플러그인

Master / Detail · detailPlugin

상세 버튼 아래에 별도 ZixoGrid 인스턴스를 만들고 해당 부모의 하위 데이터를 표시합니다. 예제는 부모·상세 각각 100행을 사용합니다.

설치 함수: detailPlugin(config) · 해제 이름: detail

설정 속성 · 기본값

필수값은 해당 입력에서 반드시 제공하는 값입니다. 조건부 필수값은 설명에 적힌 기능을 사용할 때 필요합니다. 표시가 없는 옵션은 생략할 수 있으며 기본값을 사용합니다. 빈 문자열·누락과 0·false를 혼동하지 마세요.

설정 위치와 동작
설정기본값설명
field 필수값없음상세 버튼을 표시할 컬럼 field입니다. 보통 editable:false로 구성합니다.

상세 버튼을 표시할 부모 컬럼의 field를 지정합니다.

columns하위 컬럼 배열하위 그리드에 사용할 컬럼을 전달합니다.
rows없음rows(parentRow)가 로컬 하위 배열을 반환합니다.
loadRows없음loadRows(parentRow,{signal})이 하위 배열을 비동기 반환합니다. 조회 결과를 부모 키별로 캐시하며 로컬 rows보다 우선 사용합니다.

사용 순서

  1. 상세 버튼 컬럼을 부모 columns에 추가합니다.
  2. 하위 columns와 rows(parentRow) 또는 loadRows(parentRow,{signal})를 선언합니다.
  3. 상세 버튼을 누르면 별도 그리드가 생성됩니다. 같은 상세를 접거나 부모를 종료하면 하위 인스턴스를 정리합니다.

연결 API

플러그인 또는 연결된 Core API
호출 / 인수반환값동작사용 예
grid.toggleDetail(id)Promise<void>부모 행의 상세를 접거나 펼칩니다. 비동기 조회를 포함하며 Promise를 반환합니다.grid.toggleDetail(id)

필수값 grid.toggleDetail 인수: id

설치 · 해제 · Vue 3 수명주기

생성 시 plugins에 등록하거나, 아직 설치하지 않은 인스턴스에서 grid.install을 호출합니다. 같은 name은 중복 설치할 수 없습니다. 설정에 쓰는 데이터·함수는 아래 전체 예제 소스를 참고하세요.

grid.install(detailPlugin({
      field: 'detail',
      columns: [
        { field: 'name', title: '하위 주문', width: 300 },
        {
          field: 'scrollRowKey',
          title: '참조 · 행 키',
          width: 720,
          editable: false,
          sortable: false,
          format: (_value, row) => String(row.id ?? row.code ?? row.name ?? ''),
        },
        {
          field: 'scrollName',
          title: '참조 · 이름',
          width: 720,
          editable: false,
          sortable: false,
          format: (_value, row) => String(row.name ?? ''),
        },
        {
          field: 'scrollCategory',
          title: '참조 · 분류 / 지역',
          width: 720,
          editable: false,
          sortable: false,
          format: (_value, row) =>
            String(row.category ?? row.region ?? row.status ?? ''),
        },
        {
          field: 'scrollNote',
          title: '참조 · 상세 문구',
          width: 720,
          editable: false,
          sortable: false,
          format: (_value, row) =>
            String(row.note ?? row.description ?? row.name ?? ''),
        },
      ],
      rows: createDetailRows,
    }));
grid.uninstall('detail'); // 플러그인 해제
// Vue 3 컴포넌트가 종료될 때
onBeforeUnmount(() => grid.destroy());

조회 AbortController를 취소하고 생성된 하위 그리드를 destroy합니다. 상세 렌더러·변환·render 리스너를 제거하고 이전 toggleDetail을 복원합니다. 재렌더/가상화 이탈 시에도 하위 인스턴스를 정리합니다.

관련 이벤트

  • render
  • error

이벤트 구독은 grid.on(name, (detail, grid) => {})를 사용합니다. 해제 함수 또는 grid.off로 구독을 정리하세요.

실행 예제 · 실제 원본

아래 소스는 examples/features/detail.js와 Vue 원본입니다. 해당 예제 경로를 기준으로 import하며, 행 데이터는 별도 파일을 함께 사용합니다.

JavaScript 전체 소스 · detail.js
import { ZixoGrid, detailPlugin } from '../../src/index.js';
import { rows, createDetailRows } from './detail.data.js';

const host = document.querySelector('#grid'),
  report = document.querySelector('#report');

const options = {
  rowKey: 'id',
  height: 360,
  rows: rows,
  columns: [
    { field: 'name', title: '주문', width: 240 },
    { field: 'qty', title: '수량', type: 'number', parse: Number },
    { field: 'price', title: '단가', type: 'number', parse: Number },
    { field: 'detail', title: '상세', editable: false },
    {
      field: 'scrollRowKey',
      title: '참조 · 행 키',
      width: 360,
      editable: false,
      sortable: false,
      format: (_value, row) => String(row.id ?? row.code ?? row.name ?? ''),
    },
    {
      field: 'scrollName',
      title: '참조 · 이름',
      width: 360,
      editable: false,
      sortable: false,
      format: (_value, row) => String(row.name ?? ''),
    },
    {
      field: 'scrollCategory',
      title: '참조 · 분류 / 지역',
      width: 360,
      editable: false,
      sortable: false,
      format: (_value, row) =>
        String(row.category ?? row.region ?? row.status ?? ''),
    },
    {
      field: 'scrollNote',
      title: '참조 · 상세 문구',
      width: 360,
      editable: false,
      sortable: false,
      format: (_value, row) =>
        String(row.note ?? row.description ?? row.name ?? ''),
    },
  ],
  plugins: [
    detailPlugin({
      field: 'detail',
      columns: [
        { field: 'name', title: '하위 주문', width: 300 },
        {
          field: 'scrollRowKey',
          title: '참조 · 행 키',
          width: 720,
          editable: false,
          sortable: false,
          format: (_value, row) => String(row.id ?? row.code ?? row.name ?? ''),
        },
        {
          field: 'scrollName',
          title: '참조 · 이름',
          width: 720,
          editable: false,
          sortable: false,
          format: (_value, row) => String(row.name ?? ''),
        },
        {
          field: 'scrollCategory',
          title: '참조 · 분류 / 지역',
          width: 720,
          editable: false,
          sortable: false,
          format: (_value, row) =>
            String(row.category ?? row.region ?? row.status ?? ''),
        },
        {
          field: 'scrollNote',
          title: '참조 · 상세 문구',
          width: 720,
          editable: false,
          sortable: false,
          format: (_value, row) =>
            String(row.note ?? row.description ?? row.name ?? ''),
        },
      ],
      rows: createDetailRows,
    }),
  ],
};

const grid = new ZixoGrid(host, options);

window.addEventListener('pagehide', () => grid.destroy(), { once: true });
grid.on(
  'validationerror',
  (detail) => (report.textContent = detail.error.message),
);

report.textContent = '상세 버튼으로 하위 그리드를 생성하고 접으면 해제합니다.';
Vue 3 전체 소스 · vue-detail.vue
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue';
import { ZixoGrid, detailPlugin } from '../../src/index.js';
import { rows, createDetailRows } from './detail.data.js';

// Instance and DOM belong to this component, never to window or a global ID.
const gridHost = ref(null);
const message = ref('');

let grid;
let alive = true;

onMounted(() => {
  const options = {
    rowKey: 'id',
    height: 360,
    rows: rows,
    columns: [
      { field: 'name', title: '주문', width: 240 },
      { field: 'qty', title: '수량', type: 'number', parse: Number },
      { field: 'price', title: '단가', type: 'number', parse: Number },
      { field: 'detail', title: '상세', editable: false },
      {
        field: 'scrollRowKey',
        title: '참조 · 행 키',
        width: 360,
        editable: false,
        sortable: false,
        format: (_value, row) => String(row.id ?? row.code ?? row.name ?? ''),
      },
      {
        field: 'scrollName',
        title: '참조 · 이름',
        width: 360,
        editable: false,
        sortable: false,
        format: (_value, row) => String(row.name ?? ''),
      },
      {
        field: 'scrollCategory',
        title: '참조 · 분류 / 지역',
        width: 360,
        editable: false,
        sortable: false,
        format: (_value, row) =>
          String(row.category ?? row.region ?? row.status ?? ''),
      },
      {
        field: 'scrollNote',
        title: '참조 · 상세 문구',
        width: 360,
        editable: false,
        sortable: false,
        format: (_value, row) =>
          String(row.note ?? row.description ?? row.name ?? ''),
      },
    ],
    plugins: [
      detailPlugin({
        field: 'detail',
        columns: [
          { field: 'name', title: '하위 주문', width: 300 },
          {
            field: 'scrollRowKey',
            title: '참조 · 행 키',
            width: 720,
            editable: false,
            sortable: false,
            format: (_value, row) =>
              String(row.id ?? row.code ?? row.name ?? ''),
          },
          {
            field: 'scrollName',
            title: '참조 · 이름',
            width: 720,
            editable: false,
            sortable: false,
            format: (_value, row) => String(row.name ?? ''),
          },
          {
            field: 'scrollCategory',
            title: '참조 · 분류 / 지역',
            width: 720,
            editable: false,
            sortable: false,
            format: (_value, row) =>
              String(row.category ?? row.region ?? row.status ?? ''),
          },
          {
            field: 'scrollNote',
            title: '참조 · 상세 문구',
            width: 720,
            editable: false,
            sortable: false,
            format: (_value, row) =>
              String(row.note ?? row.description ?? row.name ?? ''),
          },
        ],
        rows: createDetailRows,
      }),
    ],
  };

  grid = new ZixoGrid(gridHost.value, options);

  grid.on(
    'validationerror',
    (detail) => (message.value = detail.error.message),
  );

  message.value = '상세 버튼으로 하위 그리드를 생성하고 접으면 해제합니다.';
});
onBeforeUnmount(() => {
  alive = false;
  grid?.destroy(); // Cancels grid requests, removes events and releases plugins.
  grid = undefined;
});
defineExpose({ getGrid: () => grid });
</script>

<template>
  <main>
    <h1>Master / Detail</h1>

    <div ref="gridHost" class="vue-grid-host"></div>
    <div class="notes result" aria-live="polite">{{ message }}</div>
  </main>
</template>

제약 사항 · 주의할 동작

  • 하위 rowKey는 id이며 하위 그리드는 이 플러그인이 전달하는 columns/rows로 구성됩니다. 임의 하위 plugins 설정은 현재 제공하지 않습니다.
  • 상세 영역은 고정 높이 180px, 하위 그리드는 160px로 생성합니다.
  • lazy 조회 실패 시 error와 재시도를 사용하며 여러 동시 요청의 공유 상태 표시에 제한이 있습니다.

연결 기능 메뉴

Master / Detail · Tree / Detail lazy 요청

이 플러그인 상세 페이지 독립 열기 ↗ · 플러그인 설명 전체