日々ブログ

当サイトは、アフィリエイトプログラムにより商品をご紹介しています

【プログラミングのお話】ag-gridで編集可能な表を簡単に作成する

reactで遊んでいるうちにエクセルみたいな値を編集できる表を作りたいなと思い、 ライブラリを探しているうちにいい感じのライブラリを見つけたので紹介します。
表を実現するライブラリはたくさんあるのですが、いくつか触ったうちで一番使いか易くかつ高機能でした。
今回は、Reactをベースにag-gridの使い方についてまとめたいと思います。

ag-gridとは

ag-gridですが、編集可能な表を実現できるライブラリです。
他にも、列ごとのフィルターだったり、ソート機能だったり、便利な機能がたくさんついた表を簡単に作成することができます。
意外とこういう編集可能な表って無いんですよね~。

まずは表示する

まずは、公式のデモページのとおり表示します。
表で表示したいデータをuseStateでリアクティブな変数にするのがポイントですね。
上の図の通り、各セルをクリックして選択することができます。
基本のコードカスタマイズして色んな機能を足していきます。

www.ag-grid.com

import { AgGridReact } from "ag-grid-react";
import { useState } from "react";

import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';

const AgGridApp=()=>{
    const [rowData,setRowData] = useState([
        {make: "Toyota", model: "Celica", price: 35000},
        {make: "Ford", model: "Mondeo", price: 32000},
        {make: "Porsche", model: "Boxster", price: 72000}
    ]);
    
    const [columnDefs] = useState([
        { field: 'make' },
        { field: 'model' },
        { field: 'price' }
    ])
    return(
        <>
        This is AgGridApp
        <div className="ag-theme-alpine" style={{height: 400, width: 600}}>
           <AgGridReact
               rowData={rowData}
               columnDefs={columnDefs}>
           </AgGridReact>
       </div>

        </>
    )
}

export default AgGridApp;

データを更新する

useStateで宣言した変数を変更すれば自動的に表の内容も更新されます。 サーバーからデータを取得して更新したい場合などに便利ですね。 なお、わかりやすさを優先してボタンっぽく動くdivを作成してクリック時にデータの内容を空白にするように変更しております。

import { AgGridReact } from "ag-grid-react";
import { useState } from "react";
import { render } from 'react-dom';

import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import "./style.scss"

const AgGridApp=()=>{
    const [rowData,setRowData] = useState([
        {make: "Toyota", model: "Celica", price: 35000},
        {make: "Ford", model: "Mondeo", price: 32000},
        {make: "Porsche", model: "Boxster", price: 72000}
    ]);
    
    const [columnDefs] = useState([
        { field: 'make' },
        { field: 'model' },
        { field: 'price' }
    ])
    const onClickDataChange=()=>{
        setRowData([])
    }
    return(
        <>
        This is AgGridApp
        <div className="ag-theme-alpine" style={{height: 400, width: 600}}>
           <AgGridReact
               rowData={rowData}
               columnDefs={columnDefs}>
           </AgGridReact>
       </div>
       <div className="c-button" onClick={()=>{onClickDataChange()}}>
        データ変更
       </div>
        </>
    )
}

export default AgGridApp;

style.scssの中身はこんな感じです。
普通にボタン属性で宣言しても大丈夫です。

.c-button{
    width: 180px;
    text-align: center;
    border-radius: 5px;
    cursor: pointer;
    background-color: gray;
    border:1px solid black;
}

ネットワーク経由でデータを取得する

ネットワーク経由でデータを取得する場合は、useEffectあるいはuseCallbackを用いて取得しましょう。
データを取得する前に表示が完了してしまう可能性があるので、onGridReadyのトリガで実行します。
これまた、公式のデモの通り記述します。
また、もともと表示しているデータと列の定義が変更する場合は、再定義しましょう。

import { AgGridReact } from "ag-grid-react";
import { useState,useCallback } from "react";

import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import "./style.scss"

const AgGridApp=()=>{
    const [rowData,setRowData] = useState([
    ]);
    
    const [columnDefs,setColumnDef] = useState([])
    const columnDefsMedalsIncluded = [
        { field: 'athlete' },
        { field: 'gold' },
        { field: 'silver' },
        { field: 'bronze' },
        { field: 'total' },
        { field: 'age' },
        { field: 'country' },
        { field: 'sport' },
        { field: 'year' },
        { field: 'date' },
      ];

    const onGridReady = useCallback((params) => {
        fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
          .then((resp) => resp.json())
          .then((data) => setRowData(data));
          // 列ヘッダの定義が当初と変わっているので再定義
          setColumnDef(columnDefsMedalsIncluded)
      }
      , []);

    
    return(
        <>
        This is AgGridApp
        <div className="ag-theme-alpine" style={{height: 400, width: 600}}>
           <AgGridReact
               rowData={rowData}
               columnDefs={columnDefs}
               onGridReady={onGridReady}
               >
           </AgGridReact>
       </div>

        </>
    )
}

export default AgGridApp;

編集可能にする

各列の設定内容を定義してやることで実現できます。
editable属性をtrueにすることで、ダブルクリックすると編集可能になります。
デフォルト設定では、編集できないようになっているのでダブルクリックしても反応がありません。

import { AgGridReact } from "ag-grid-react";
import { useState,useCallback } from "react";

import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import "./style.scss"

const AgGridApp=()=>{
    const [rowData,setRowData] = useState([
    ]);
    
    const [columnDefs,setColumnDef] = useState([])
    const columnDefsMedalsIncluded = [
        { field: 'athlete',editable:true},
        { field: 'gold' },
        { field: 'silver' },
        { field: 'bronze',editable:true},
        { field: 'total' },
        { field: 'age' },
        { field: 'country' },
        { field: 'sport' },
        { field: 'year' },
        { field: 'date' },
      ];

    const onGridReady = useCallback((params) => {
        fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
          .then((resp) => resp.json())
          .then((data) => setRowData(data));
          // 列ヘッダの定義が当初と変わっているので再定義
          setColumnDef(columnDefsMedalsIncluded)
      }
      , []);

    
    return(
        <>
        This is AgGridApp
        <div className="ag-theme-alpine" style={{height: 400, width: 600}}>
           <AgGridReact
               rowData={rowData}
               columnDefs={columnDefs}
               onGridReady={onGridReady}
               >
           </AgGridReact>
       </div>

        </>
    )
}

export default AgGridApp;
面倒な場合はデフォルト設定を変更する

編集するデータが増えるたびに設定を追加するのは面倒ですよね。
そうした場合は、デフォルト設定で編集可能な状態にするのが楽です。
各列で別途列を定義した場合デフォルトの内容は上書きされます。
なので、上の場合はAtheleteの列が編集不可です。

編集したデータを確認する

rowDataに対して、useState変数を渡していたので、自動で反映されています。
めちゃくちゃ便利ですね。
例では、1行目のgoldのフィールドの値を取得して画面に表示しています。
ここまでの内容を一旦公開しておきました。
今後更新するたびにアプリ側も更新します。

http://sample-react-app-hibiblog.s3-website-ap-northeast-1.amazonaws.com/

import { AgGridReact } from "ag-grid-react";
import { useState,useCallback } from "react";

import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import "./style.scss"

const AgGridApp=()=>{
    const [rowData,setRowData] = useState([
    ]);

    const [targetText,setTargetText]=useState("")
    
    const [columnDefs,setColumnDef] = useState([])
    const columnDefsMedalsIncluded = [
        { field: 'athlete',editable:false},
        { field: 'gold' },
        { field: 'silver' },
        { field: 'bronze',editable:true},
        { field: 'total' },
        { field: 'age' },
        { field: 'country' },
        { field: 'sport' },
        { field: 'year' },
        { field: 'date' },
      ];

    const defaultColDef={
        editable:true,
    }

    const onGridReady = useCallback((params) => {
        fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
          .then((resp) => resp.json())
          .then((data) => setRowData(data));
          // 列ヘッダの定義が当初と変わっているので再定義
          setColumnDef(columnDefsMedalsIncluded)
      }
      , []);
    
    const onClickRowDataOutput=()=>{
        console.log(rowData)
    }
    
    const onCellValueChanged=(event)=>{
        console.log(rowData)
        setTargetText(String(rowData[0]['gold']))
    }
    return(
        <>
        This is AgGridApp
        <div className="ag-theme-alpine" style={{height: 400, width: 600}}>
           <AgGridReact
                defaultColDef={defaultColDef}
               rowData={rowData}
               columnDefs={columnDefs}
               onGridReady={onGridReady}
               onCellValueChanged={(event)=>{onCellValueChanged(event)}}
               >
           </AgGridReact>
       </div>
       

       <button onClick={()=>{onClickRowDataOutput()}}>
        表のデータをコンソールに表示
       </button>
       <div>
        1行目のgold<br></br>
        {targetText}
        </div>
        </>
    )
}

export default AgGridApp;