Skip to main content Skip to footer

Disable a PivotPanel Checkbox Field in Vue

PivotPanel with disabled checkboxes

Background:

Sometimes, you may want to disable the checkbox of a specific field in your PivotPanel to prevent users from interacting with it.

Steps to Complete:

  1. Create a PivotEngine (data + fields)
  2. Track which fields to disable
  3. Render PivotPanel + PivotGrid and keep a ref to the panel
  4. Disable checkboxes via the fields grid’s formatItem

Getting Started:

Create a PivotEngine (data + fields)

Engine shared by the Panel and Grid.

/* src/olap/engine.ts */
import * as wjOlap from '@mescius/wijmo.olap';

export type OrderRow = {
  Country: string; Category: string; Product: string;
  Amount: number; Quantity: number; Date: Date;
};

export function makeData(rows = 200): OrderRow[] {
  const countries = ['US', 'UK', 'Germany', 'Japan'];
  const cats = ['Electronics', 'Clothing'];
  const prods: Record<string, string[]> = {
    Electronics: ['Phone', 'Laptop', 'Tablet'],
    Clothing: ['Shirt', 'Jacket', 'Jeans'],
  };
  const out: OrderRow[] = [];
  for (let i = 0; i < rows; i++) {
    const c = countries[(Math.random() * countries.length) | 0];
    const cat = cats[(Math.random() * cats.length) | 0];
    const p = prods[cat][(Math.random() * prods[cat].length) | 0];
    out.push({
      Country: c, Category: cat, Product: p,
      Amount: +(Math.random() * 1000).toFixed(2),
      Quantity: 1 + ((Math.random() * 10) | 0),
      Date: new Date(2024, (Math.random() * 12) | 0, 1 + ((Math.random() * 28) | 0)),
    });
  }
  return out;
}

export function createEngine(): wjOlap.PivotEngine {
  return new wjOlap.PivotEngine({
    itemsSource: makeData(),
    fields: [
      { binding: 'Country', header: 'Country' },
      { binding: 'Category', header: 'Category' },
      { binding: 'Product',  header: 'Product'  },
      { binding: 'Amount',   header: 'Amount',   format: 'n2' },
      { binding: 'Quantity', header: 'Quantity' },
      { binding: 'Date',     header: 'Date'     },
    ],
    rowFields: ['Country'],
    valueFields: ['Amount', 'Quantity'],
  });
}

 

Track which fields to disable

Reactive list of protected field headers. Checks against these names in the formatter.

<!-- src/App.vue -->
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import * as wjOlap from '@mescius/wijmo.olap';
import { createEngine } from './olap/engine';
import * as WjOlapVue from '@mescius/wijmo.vue2.olap'; // <wj-pivot-panel>, <wj-pivot-grid>

const engine = createEngine();
const disabledFields = ref<string[]>(['Amount']); // edit as needed
</script>

 

Render PivotPanel + PivotGrid and keep a ref to the panel

Bind both to the same engine; ref gives access to the underlying control. Wijmo Vue components expose the control via ref, letting you call ref.value.control.

<!-- src/App.vue -->
<template>
  <div class="wrap">
    <h2>Wijmo PivotPanel (Vue) — Disable specific field checkboxes</h2>

    <!-- panel + grid bound to the same engine -->
    <wj-pivot-panel ref="panelRef" :items-source="engine"></wj-pivot-panel>
    <wj-pivot-grid  :items-source="engine"></wj-pivot-grid>

    <p>Disabled fields: <code>{{ disabledFields.join(', ') }}</code></p>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue';
import * as wjOlap from '@mescius/wijmo.olap';
import { createEngine } from './olap/engine';
import * as WjOlapVue from '@mescius/wijmo.vue2.olap'; // registers components
const engine = createEngine();
const disabledFields = ref<string[]>(['Amount']);
const panelRef = ref<any>(null);
</script>

<style scoped>
.wrap { font-family: system-ui, Segoe UI, Arial; padding: 16px; }
wj-pivot-panel, wj-pivot-grid { display: block; margin: 12px 0; }
</style>

 

Disable checkboxes via the fields grid’s formatItem

Hook the internal fields list grid (_lbFields) formatItem and disable the <input type="checkbox"> when the field header matches.

<!-- still in src/App.vue (add to <script setup>) -->
<script setup lang="ts">
/* ...existing imports/refs... */

onMounted(() => {
  const panel = panelRef.value?.control as wjOlap.PivotPanel | undefined;
  const fieldsGrid = (panel as any)?._lbFields; // private; version-sensitive
  if (!fieldsGrid?.formatItem) return;

  const handler = (s: any, e: any) => {
    if (e.panel === s.cells) {
      const row = e.getRow();
      const col = e.getColumn();
      const header = row?.dataItem?.[col?.binding];
      if (disabledFields.value.includes(header)) {
        const chk: HTMLInputElement | null = e.cell.querySelector('input[type="checkbox"]');
        if (chk) chk.disabled = true; // keep protected fields non-toggleable
      }
    }
  };

  fieldsGrid.formatItem.addHandler(handler);
  // optional: cleanup on unmount
  addEventListener('beforeunload', () => fieldsGrid.formatItem.removeHandler(handler));
});
</script>

 

If implemented correctly, your checkboxes should now be disabled/protected. I hope you find this article helpful. Happy coding!

 

Full code file to reference:

<!-- src/App.vue -->
<template>
  <div class="wrap">
    <h2>Wijmo PivotPanel (Vue) — Disable specific field checkboxes</h2>

    <!-- Bind Panel + Grid to the same engine -->
    <wj-pivot-panel ref="panelRef" :items-source="engine"></wj-pivot-panel>
    <wj-pivot-grid  :items-source="engine"></wj-pivot-grid>

    <p>Disabled fields: <code>{{ disabledFields.join(', ') }}</code></p>
  </div>
</template>

<script setup lang="ts">
/**
 * import CSS here: ensures Wijmo styles load without requiring edits to main.ts.
 */
import '@mescius/wijmo.styles/wijmo.css';

import { ref, onMounted, onBeforeUnmount } from 'vue';
import * as wjOlap from '@mescius/wijmo.olap';
import '@mescius/wijmo.vue2.olap'; // registers <wj-pivot-panel> / <wj-pivot-grid>

/** ---------- Demo data + engine helpers ---------- */
type OrderRow = {
  Country: string; Category: string; Product: string;
  Amount: number; Quantity: number; Date: Date;
};

function makeData(rows = 200): OrderRow[] {
  const countries = ['US', 'UK', 'Germany', 'Japan'];
  const cats = ['Electronics', 'Clothing'];
  const prods: Record<string, string[]> = {
    Electronics: ['Phone', 'Laptop', 'Tablet'],
    Clothing: ['Shirt', 'Jacket', 'Jeans'],
  };
  const out: OrderRow[] = [];
  for (let i = 0; i < rows; i++) {
    const c = countries[(Math.random() * countries.length) | 0];
    const cat = cats[(Math.random() * cats.length) | 0];
    const p = prods[cat][(Math.random() * prods[cat].length) | 0];
    out.push({
      Country: c,
      Category: cat,
      Product: p,
      Amount: +(Math.random() * 1000).toFixed(2),
      Quantity: 1 + ((Math.random() * 10) | 0),
      Date: new Date(2024, (Math.random() * 12) | 0, 1 + ((Math.random() * 28) | 0)),
    });
  }
  return out;
}

function createEngine(): wjOlap.PivotEngine {
  return new wjOlap.PivotEngine({
    itemsSource: makeData(),
    fields: [
      { binding: 'Country', header: 'Country' },
      { binding: 'Category', header: 'Category' },
      { binding: 'Product',  header: 'Product'  },
      { binding: 'Amount',   header: 'Amount',   format: 'n2' },
      { binding: 'Quantity', header: 'Quantity' },
      { binding: 'Date',     header: 'Date'     },
    ],
    rowFields: ['Country'],
    valueFields: ['Amount', 'Quantity'],
  });
}

/** ---------- State ---------- */
const engine = createEngine();
const disabledFields = ref<string[]>(['Amount']); // single source of truth for protected fields
const panelRef = ref<any>(null);

/** ---------- Disable checkboxes in the PivotPanel fields list ---------- */
let detachFormatItem: null | (() => void) = null;
onMounted(() => {
  const panel = panelRef.value?.control as wjOlap.PivotPanel | undefined;
  const fieldsGrid = (panel as any)?._lbFields; // internal fields list grid hosts the checkboxes
  if (!fieldsGrid?.formatItem) return;

  const handler = (s: any, e: any) => {
    if (e.panel === s.cells) {
      const row = e.getRow();
      const col = e.getColumn();
      const header = row?.dataItem?.[col?.binding];
      if (disabledFields.value.includes(header)) {
        const chk: HTMLInputElement | null = e.cell.querySelector('input[type="checkbox"]');
        if (chk) chk.disabled = true; // prevent toggling protected fields
      }
    }
  };

  fieldsGrid.formatItem.addHandler(handler);
  detachFormatItem = () => fieldsGrid.formatItem.removeHandler(handler);
});

/** ---------- Optional: block “Remove Field” via context menu ---------- */
let unpatchMenu: null | (() => void) = null;
onMounted(() => {
  const panel = panelRef.value?.control as wjOlap.PivotPanel | undefined;
  if (!panel) return;

  try {
    const MenuProto = (wjOlap as any)._ListContextMenu?.prototype;
    if (MenuProto && !MenuProto.__patched && MenuProto._canExecute) {
      const original = MenuProto._canExecute;
      MenuProto._canExecute = function (this: any, cmd: string) {
        const isRemove = /remove(field)?/i.test(cmd ?? '');
        const header = this?._targetField?.header;
        const owner = (this?.ownerPivotPanel ?? this?.hostPivotPanel)?.owner;
        if (isRemove && header && owner?.disabledFields?.includes(header)) return false;
        return original.apply(this, arguments as any);
      };
      MenuProto.__patched = true;

      // expose disabledFields to the menu instance (best-effort; private API)
      (panel as any).owner = { disabledFields: disabledFields.value };

      unpatchMenu = () => {
        // guard against double-unpatch or version differences
        try { if (MenuProto._canExecute && original) MenuProto._canExecute = original; } catch {}
      };
    }
  } catch {
    // ignore if internals differ
  }
});

/** ---------- Cleanup ---------- */
onBeforeUnmount(() => {
  try { detachFormatItem?.(); } catch {}
  try { unpatchMenu?.(); } catch {}
});
</script>

<style scoped>
.wrap { font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; padding: 16px; }
wj-pivot-panel, wj-pivot-grid { display: block; margin: 12px 0; }
</style>

Andrew Peterson

Technical Engagement Engineer