Vuetifyで魅せる!データ可視化

はじめに

こんにちは、フロントエンドエンジニアのraraya99です。 近年のフロントエンド開発は、数多くのライブラリやフレームワークを活用することで、効率的かつ高品質なプロジェクト制作が可能となっています。 前回の記事でVueUseについて紹介させていただきました。

今回はコンポーネントフレームワーク Vuetifyv-data-table に焦点を当てます。私たちのプロジェクトでは、Vuetify の様々なコンポーネントを直接使用せず、独自の UI デザインを適用するためにラップして活用しています。そのため、Vuetify の持つ柔軟性を活かしつつ、プロジェクト固有のデザイン要件に対応しています。(今回の記事では便宜上、ラップせず直接使用します。)

v-data-table は、データの整理・表示に優れた、柔軟なコンポーネントです。基本機能だけでも美しいデータテーブルを簡単に作成できますが、その中でも 「複数行ヘッダー」 に注目します。複雑なデータ表示に適したこの機能を活用することで、さらに読みやすく、直感的なデザインが可能になります。vuetifyにはv-data-tableと似たv-tableというコンポーネントも存在しますが今回は触れません。(下記で比較だけいたします)

v-data-tableとv-tableの比較

Vuetify には、v-data-table と似た v-table というコンポーネントも存在します。 どちらを選ぶかは、プロジェクトの要件によって決まります。

v-table: 静的なテーブルの表示に適しており、軽量でシンプルな実装が可能です。 v-data-table: 動的なテーブルの操作に適しており、ソート・検索・ページネーション・グループ化・行選択などの機能を備えています。

なぜ複数行ヘッダーに注目したのか?

業務の中で複雑なデータを扱う際、ヘッダーの情報量が多く2行から3行に増やして整理したいという課題がありました。 試してみたところ簡単に実装できたため、この記事では以下の流れで紹介します。

  1. v-data-table の基本的な使い方
  2. 複数行ヘッダーの構築方法
  3. さらに複雑なヘッダー構造の実現例
  4. その他の便利な機能の活用(おまけ)

v-data-tableの基本的な使い方

まずは最小構成の1行ヘッダーからスタートします。

ヘッダー1行

ページネーションも自動で追加され、デフォルトで高い完成度を持っています。

ヘッダーを2行にする方法

複数行ヘッダーを実現するには、children プロパティを利用します。親ヘッダーの下に子ヘッダーを定義することで、階層構造を作成できます。

ヘッダー2行

children プロパティを使うだけで、見やすく整理された2行ヘッダーを簡単に作成できます。 「特徴」という親ヘッダーの下に「種」と「タイプ」、「生息地」が配置され、構造が明確になります。

中略

  const headers = [
    {
      title: '基本情報',
      children: [
        { title: '名前', value: 'name' },
        { title: '重さ (kg)', value: 'weight' },
      ],
    },
    {
      title: '特徴',
      children: [
        { title: '種', value: 'species' },
        { title: 'タイプ', value: 'type' },
        { title: '生息地', value: 'habitat' },
      ],
    },
  ]

  const items = [
    {
      name: 'ピカチュウ',
      weight: 6,
      species: 'ネズミポケモン',
      type: 'でんき',
      habitat: '森',
    },
    {

中略

さらに複雑なヘッダー構成

ヘッダーを3行以上に拡張することも簡単です。children をさらにネストすることで、より深い階層構造を作成できます。

ヘッダー3行

この柔軟性は、公式の型定義からも確認できます。children プロパティが 再帰的に DataTableHeader 型を参照 しています。

type DataTableHeader<T = Record<string, any>> = {
    key?: 'data-table-group' | 'data-table-select' | 'data-table-expand' | (string & {});
    value?: SelectItemKey<T>;
    title?: string;
    fixed?: boolean;
    align?: 'start' | 'end' | 'center';
    width?: number | string;
    minWidth?: string;
    maxWidth?: string;
    nowrap?: boolean;
    headerProps?: Record<string, any>;
    cellProps?: HeaderCellProps;
    sortable?: boolean;
    sort?: DataTableCompareFunction;
    sortRaw?: DataTableCompareFunction;
    filter?: FilterFunction;
    mobile?: boolean;
    children?: DataTableHeader<T>[];
};

せかっくなので10行でためしてみました

その他の便利な機能

予想に反してさくっと3行のヘッダーができてしまったため、現時点では弊社サービス Re:lation で採用していないものの、今後活用できそうなv-data-tableのcell-props を紹介します。

cell-props を使うと、セル単位でスタイルを動的に変更でき、条件に応じたカスタマイズが柔軟に行えます。

実装例

セルのスタイルを動的に変更する

getCellProps を利用して、特定の条件に基づいてセルや列全体にスタイルを適用します。

<script setup lang="ts">
  const headers = [
    { title: 'Name', value: 'name' },
    { title: 'Price', value: 'price' },
  ];
  const items = [
    { name: 'りんご',    price: 120 },
    { name: 'キウイ',    price: 40 },
    { name: 'バナナ',    price: 80 },
    { name: 'ぶどう',    price: 100 },
    { name: 'さくらんぼ', price: 150 },
  ]

  // セルのプロパティを取得
  const getCellProps = (cell: any) => {
    const classes: string[] = [];
    const styles: Record<string, string> = {};

    // 列のスタイルを指定
    if (cell.column.key === 'name') {
      classes.push('name-column');
    }

    // セルのスタイルを指定
    if (cell.column.value === 'price') {
      if (cell.value >= 150) {
        classes.push('high-price');
        styles.color = 'red';
        styles.fontWeight = 'bold';
      } else if (cell.value >= 100) {
        classes.push('high-price');
      }
    }

    return {
      class: classes.join(' '),
      style: Object.keys(styles).length ? styles : undefined,
    };
  };
</script>

<template>
  <v-data-table :items="items" :headers="headers" :cell-props="getCellProps" />
</template>

<style>
  .name-column {
    font-weight: bold;
  }
  .high-price {
    background-color: #ffeeee;
  }
</style>

まとめ

v-data-table は、ヘッダーを簡単に複数行に拡張できる柔軟性や、cell-props を使った動的カスタマイズが魅力です。

さらに、標準搭載のソートや検索機能に加え、使い込むほど便利な機能が発掘できる奥深さがあります。

他にも便利な機能やコンポーネントがありましたら、ぜひ教えていただけると嬉しいです!