[Wijmo入門]FlexGridの使い方 – 活用編(6) – イベントを利用したデータ検証

業務アプリケーションの様々な要件に対応できるUI部品を備えたJavaScriptライブラリ「Wijmo(ウィジモ)」には、高速・軽量かつ多機能なデータグリッドコントロール「FlexGrid(フレックスグリッド)」コントロールが含まれています。

FlexGridはUIにおけるデータの一覧表示だけでなく、その編集操作も実現できる点が大きな特長です。企業内で使用され、重要な情報を取り扱うことも多い業務アプリケーションでは、このようなグリッドによるデータ編集で、入力値の正常性をチェックする「データ検証」機能が求められることも少なくありません。
FlexGridでは、このデータ検証を実現するための機能が提供されており、要件に応じて適切な方法を選択できます。

本連載では2回にわたりFlexGridにおける2つのデータ検証方法をご紹介します。前編ではCollectionViewを利用したデータ検証について解説しました。

本記事はその後編として、イベントを利用したデータ検証について詳しく解説します。

FlexGridでイベントを利用したデータ検証

開発環境の準備

この記事では以下の開発環境を使用します。

今回作成するファイルは次の4つです。

index.htmlページ本体。要素としてFlexGridコントロールを配置します
app.jsFlexGridコントロールを作成するコードを記載します
data.jsFlexGridコントロールに連結するデータを記載します
styles.css各種ページ要素のスタイルを設定するコードを記載します

Wijmoの参照設定

WijmoのFlexGridコントロールを使用するには、専用のモジュールを環境にインストールする必要があります。CDNを参照したり、npmなどから入手したりする方法もありますが、今回は環境に直接モジュールを配置していきます。あらかじめWijmoの製品版かトライアル版をご用意ください。トライアル版は以下より無償で入手可能です。

製品版、またはトライアル版をダウンロードしたら、ZIPファイルを解凍し、以下のファイルを環境にコピーします。

  • scripts/wijmo.grid.min.js
  • scripts/wijmo.min.js
  • scripts/cultures/wijmo.culture.ja.min.js
  • css/wijmo.min.css

※ なお、npmを使ってWijmoの参照を行う場合は、ヘルプの「特定のパッケージをインストールする」に記載しているように、wijmo.gridパッケージに対してinstallコマンドを実行するだけでwijmo.gridが依存するパッケージも自動的にインストールされます。

FlexGridにデータを表示する

それでは、最初にベースとなる画面を作成していきます。前述のWijmoモジュールと「app.js」、「data.js」、および「styles.css」への参照設定をHTMLファイルに追加します。
※ CDNから参照する場合はコメントアウトされている部分とライブラリの参照先を入れ替えてください。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>FlexGridコントロール活用編(6)</title>

    <!-- ローカルのライブラリを参照する場合 -->
    <link rel="stylesheet" href="css/wijmo.min.css" />
    <script src="scripts/wijmo.min.js"></script>
    <script src="scripts/wijmo.grid.min.js"></script>
    <script src="scripts/cultures/wijmo.culture.ja.min.js"></script>

    <!-- CDNからライブラリを参照する場合 -->
    <!-- <link rel="stylesheet" href="https://cdn.mescius.com/wijmo/5.20261.50/styles/wijmo.min.css" />
    <script src="https://cdn.mescius.com/wijmo/5.20261.50/controls/wijmo.min.js"></script>
    <script src="https://cdn.mescius.com/wijmo/5.20261.50/controls/wijmo.grid.min.js"></script>
    <script src="https://cdn.mescius.com/wijmo/5.20261.50/controls/cultures/wijmo.culture.ja.min.js"></script> -->

    <link rel="stylesheet" href="css/styles.css">
    <script src="scripts/app.js"></script>
    <script src="scripts/data.js"></script>
</head>
<body>
    <div>
        <fieldset>
            <legend>編集時に検証エラーが発生した場合の動作</legend>
            <label><input type="checkbox" id="cbKeepEditMode" checked>編集モードを維持する</label>
        </fieldset>
    </div>
    <div id="theGrid"></div>
    
</body>
</html>

続いて「app.js」と「data.js」を次のように作成します。
表示するデータはFlexGridクラスのコンストラクタ内のitemsSourceプロパティで設定しています。前回の活用編(5)ではCollectionViewインスタンスをitemsSourceに設定していましたが、今回は配列データを直接設定しています。どちらの方法で設定してもかまいません。
※ ライセンスキーを設定しない場合トライアル版を示すメッセージが表示されます。ライセンスキーの入手や設定方法についてはこちらをご覧ください。

wijmo.setLicenseKey('ここにWijmoのライセンスキーを設定します');

document.readyState === 'complete' ? init() : window.onload = init;
function init() {
    // FlexGridコントロールの生成
    const theGrid = new wijmo.grid.FlexGrid('#theGrid', {
        itemsSource: getOrderData(), // 配列データを設定
        columns: [
            { binding: 'id', header: 'ID', width: 40 },
            { binding: 'ordercode', header: '受注コード', width: 120 },
            { binding: 'productname', header: '商品名', width: 180 },
            { binding: 'orderdate', header: '受注日', width: 120, format: 'yyyy/M/d' },
            { binding: 'deliverydate', header: '納品日', width: 120, format: 'yyyy/M/d' },
            { binding: 'amount', header: '金額', width: 100 },
        ]
    });
}
function getOrderData() {
  return [
    { id: 1, ordercode: "12975-BR", productname: 'コーヒーマイルド', orderdate: new Date(2026, 1, 18), deliverydate: new Date(2026, 1, 21), amount: 6400, },
    { id: 2, ordercode: "12976-AJ", productname: '果汁100%ピーチ', orderdate: new Date(2026, 1, 19), deliverydate: new Date(2026, 1, 23), amount: 1760, },
    { id: 3, ordercode: "1297-KK", productname: 'チョコクリームアイス', orderdate: new Date(2026, 1, 20), deliverydate: new Date(2026, 1, 23), amount: 25200, },
    { id: 4, ordercode: "12978-LP", productname: 'モーニングブレッド', orderdate: new Date(2026, 1, 22), deliverydate: new Date(2026, 1, 23), amount: 11200, },
    { id: 5, ordercode: "12979-xc", productname: '果汁100%レモン', orderdate: new Date(2026, 2, 10), deliverydate: new Date(2026, 2, 12), amount: -100, },
    { id: 6, ordercode: "12980-OU", productname: '果汁100%グレープ', orderdate: new Date(2026, 2, 11), deliverydate: new Date(2026, 2, 13), amount: 41600, },
    { id: 7, ordercode: "12981-QW", productname: 'ライフマーガリン', orderdate: new Date(2026, 2, 15), deliverydate: new Date(2026, 2, 18), amount: 36400, },
    { id: 8, ordercode: "12982-MV", productname: '果汁100%グレープ', orderdate: new Date(2026, 2, 16), deliverydate: new Date(2026, 2, 15), amount: 2600, },
    { id: 9, ordercode: "12983-SB", productname: 'コーヒーマイルド', orderdate: new Date(2026, 2, 18), deliverydate: new Date(2026, 2, 20), amount: 8700, },
    { id: 10, ordercode: "12984-RH", productname: '果汁100%グレープ', orderdate: new Date(2026, 2, 22), deliverydate: new Date(2026, 2, 24), amount: 10400, },
  ];
}

「styles.css」では、各種要素のスタイルを次のように定義します。

/* FlexGridのスタイル */
#theGrid {
    border: 1px solid gray;
    height: 360px;
    width: 730px;
}

/* その他の要素のスタイル */
html {
    font-family: Meiryo;
}

fieldset {
    border: 1px solid gray;
    border-radius: 8px;
    margin-bottom: 15px;
    width: 700px;
}

input {
    font-family: Meiryo;
    margin-left: 15px;
}

この状態でサンプルを実行すると、以下の画像のようにデータ設定されたFlexGridが表示されます。なお、データ検証の機能とチェックボックスの処理はこの後の章で実装していきます。

FlexGridの表示

データ検証機能を実装する

では次にデータ検証の機能を実装していきます。ここでは各列のデータに対して、以下のルールで検証を行います(前回の活用編(5)と同様です)。

検証ルール
IDなし
受注コード[数字5桁]-[英大文字2文字]
商品名なし
受注日納品日以前の日付
納品日受注日以降の日付
金額正の値

今回はセル編集の終了時に発生するFlexGridのcellEditEndingイベントを使ってデータ検証機能を実装します。cellEditEndingイベントのハンドラには下表の引数が渡され、ハンドラ内でデータ検証を実施した結果、エラーと判断した場合は編集を中止したり、値を修正して保存するといったことが可能です。

cellEditEndingイベントハンドラ引数内容
第1引数(sender)FlexGridイベント発生元のグリッド
第2引数(eventArgs)CellEditEndingEventArgs編集に関する情報
cancelプロパティ:編集の中止制御
stayInEditModeプロパティ:編集モード維持制御

なお、エラーを通知するUIはアプリケーション側で用意する必要があります。本記事ではブラウザ標準のwindow.alertやwindow.confirmを使用しますが、たとえばWijmoのPopupコントロールを使うことで、よりリッチなUIを実現することも可能です(後半の章でデモアプリケーションを紹介していますのでご参照ください)。

それでは、「app.js」に以下のコードを追加し、FlexGridのcellEditEndingイベントにハンドラを設定しましょう。

・・・(中略)・・・
    // 受注コードの形式 [数字5桁]-[英大文字2文字] になっているか判定
    function validateOrdercode(str) {
        const regex = /^\d{5}-[A-Z]{2}$/;
        return regex.test(str);
    }

    // 文字列を検証し、エラーがある場合にダイアログを表示します
    // 戻り値
    // validationResult: 'noError', 'cancel', 'newValue' 
    // newValue: 修正して保存する場合の修正値
    function validateValue(inputString, row, col) {
        let result = { validationResult: 'noError', newValue: null };

        if (col.binding == 'ordercode') {
            // 受注コード
            // [数字5桁]-[英大文字2文字]の形式になっているか確認する
            if (!validateOrdercode(inputString)) {
                // 大文字に変更することでエラーが解除される場合は変更するかどうかユーザーに確認する
                let modValue = inputString.toUpperCase();
                if (validateOrdercode(modValue)) {
                    if (window.confirm(`大文字に変更した値を登録しますか?\n変更後の値:${modValue}`)) {
                        result.validationResult = 'newValue';
                        result.newValue = modValue;
                    } else {
                        result.validationResult = 'cancel';
                    }
                } else {
                    window.alert('[数値5桁]-[英大文字2文字]の形式で入力してください。');
                    result.validationResult = 'cancel';
                }
            }

        } else if (col.binding == 'orderdate' || col.binding == 'deliverydate') {
            // 受注日・納品日
            let value = wijmo.changeType(inputString, wijmo.DataType.Date, col.format);
            if (!wijmo.isDate(value)) {
                window.alert('"yyyy/M/d"の形式で日付を入力してください。');
                result.validationResult = 'cancel';
            } else {
                let orderdate = col.binding == 'orderdate' ? value : row.dataItem.orderdate;
                let deliverydate = col.binding == 'deliverydate' ? value : row.dataItem.deliverydate;
                if (orderdate > deliverydate) {
                    window.alert('受注日が納品日以前になるように設定してください。');
                    result.validationResult = 'cancel';
                }
            }

        } else if (col.binding == 'amount') {
            // 金額
            let value = wijmo.changeType(inputString, wijmo.DataType.Number, col.format);
            if (!wijmo.isNumber(value)) {
                window.alert('数値を入力してください。');
                result.validationResult = 'cancel';
            } else if (value < 0) {
                window.alert('正の値を入力してください。');
                result.validationResult = 'cancel';
            }
        }

        return result;
    }

    // セル編集時にデータを検証する
    theGrid.cellEditEnding.addHandler((sender, eventArgs) => {
        if (eventArgs.cancel) return;//元からキャンセルの場合(Escキー押下など)は即終了する

        const result = validateValue(sender.activeEditor.value, sender.rows[eventArgs.row], sender.columns[eventArgs.col]);
        switch (result.validationResult) {
            case 'cancel':
                eventArgs.cancel = true;
                break;
            case 'newValue':
                sender.activeEditor.value = result.newValue;
                break;
            default://'noError'
                break;
        }
    });
・・・(中略)・・・

ここでは、検証を行う独自の関数としてvalidateValueを用意しています。入力された文字列(inputString)と編集対象の行(row)・列(col)の情報からデータを検証し、エラーがある場合にダイアログでユーザーに通知します。なお、編集時に入力された文字列は、FlexGridのactiveEditorプロパティのvalueから取得することが可能で、この値をvalidateValueに渡しています。

validateValue内では、各列に対応したデータ検証を行っています。
今回の例では、「受注コード」や「金額」は対象のプロパティ単体でエラーの有無を判定していますが、「受注日」と「納品日」のように、同一データアイテム内の別プロパティとの関係で判定が必要な場合もあります。そのような場合も、RowインスタンスのdataItemrow.dataItem)から他のプロパティにアクセスし、条件に応じた検証を行うことができます。

なお、各列の処理のはじめにwijmo.isDate(value)wijmo.isNumber(value)を使って判定している箇所では、値を各列の型に変換できるかどうかの判定(型解析エラーの検出)も行っています。

編集をキャンセルする

検証結果がエラーとなり、値を保存しない場合(上記のコードではvalidateValueからの戻り値がresult.validationResult = "cancel"となった場合)、cellEditEndingイベントのハンドラ内で第2引数のcancelプロパティをtrueに設定することで編集を中止します。

サンプルを実行し、「受注日」のセルを編集して「納品日」より先の日付を入力すると、ダイアログでエラーメッセージが表示されます。ダイアログを閉じると入力値がクリアされて編集前の値に戻ります。

値を修正して保存する

また今回「受注コード」では、入力されたテキストを大文字に変換することでエラーが解消する場合は、修正候補をユーザーに提示し、ユーザーがそれを採用した場合、修正した値を保存するようにしています。なお、編集中の値を変更するには、FlexGridのactiveEditorプロパティに設定されているvalueの値を書き換えます。

補足1:設定済みのデータに対する検証

cellEditEndingイベントを利用したデータ検証では、編集を完了したタイミングでのみ検証が実施されます。そのため、設定済みのデータを検証する場合は、別途処理が必要となる点にご注意ください。(本サンプルの場合、ID=3、5、8のデータはエラーを含んでいますが検出されていません。)

補足2:貼り付け操作時にも検証を行う場合

セルにテキストを入力して編集した場合だけでなく、貼り付け操作で値を設定した場合にもデータ検証を実施するには、cellEditEndingイベントで実装したのと同様の処理をpastingCellイベントでも実装する必要があります(貼り付けは編集モードを介さないため)。また、cellEditEndingイベントでは入力された文字列をactiveEditorプロパティのvalueから取得していましたが、pastingCellイベントの場合は第2引数のdataプロパティで貼り付けられた値を取得可能です。

pastingCellイベントハンドラ引数内容
第1引数(sender)FlexGridイベント発生元のグリッド
第2引数(eventArgs)CellRangeEventArgs編集に関する情報
cancelプロパティ:貼り付けの中止制御
dataプロパティ:貼り付けテキスト
・・・(中略)・・・
    // セルに値が貼り付けられた際にデータを検証する
    theGrid.pastingCell.addHandler((sender, eventArgs) => {
        if (eventArgs.cancel) return;//元からキャンセルの場合は即終了する

        const result = validateValue(eventArgs.data, sender.rows[eventArgs.row], sender.columns[eventArgs.col]);
        switch (result.validationResult) {
            case 'cancel':
                eventArgs.cancel = true;
                break;
            case 'newValue':
                eventArgs.data = result.newValue;
                break;
            default://'noError'
                break;
        }
    });
・・・(中略)・・・

編集時に検証エラーが発生した場合の動作を設定する

前の章では、セル編集時に検証エラーが発生した場合、入力値を保存せず編集前の値に戻ることを確認しました。これはcellEditEndingイベントハンドラの第2引数に設定されているstayInEditModeプロパティがfalse(デフォルト)の場合の動作です。trueに設定することで、入力した値はそのままで、保存されていない状態(編集モードが維持された状態)にすることも可能です。これによってユーザーはセルへの再入力がしやすくなります。

「app.js」に以下のコードを追加し、チェックボックスでstayInEditModeの設定を切り替えるようにしましょう。

・・・(中略)・・・
    // 編集モードを維持するかどうかを設定するチェックボックス
    const cbKeepEditMode = document.getElementById('cbKeepEditMode');    

    // セル編集時にデータを検証する
    theGrid.cellEditEnding.addHandler((sender, eventArgs) => {
        if (eventArgs.cancel) return;//元からキャンセルの場合(Escキー押下など)は即終了する

        const result = validateValue(sender.activeEditor.value, sender.rows[eventArgs.row], sender.columns[eventArgs.col]);
        switch (result.validationResult) {
            case 'cancel':
                eventArgs.cancel = true;
                // 検証エラーが発生した場合の動作を設定
                if (cbKeepEditMode.checked) {
                    eventArgs.stayInEditMode = true;
                }
                break;
            case 'newValue':
                sender.activeEditor.value = result.newValue;
                break;
            default://'noError'
                break;
        }
    });
・・・(中略)・・・

サンプルを実行して、チェックボックスをチェックしてから、セル編集で誤った値を入力すると、編集モードが維持される動作となることが確認できます。

今回ご紹介した内容については以下のデモアプリケーションで確認できます(“Run Project”をクリックするとデモが起動します)。

また、解説ではブラウザ標準のwindow.alertやwindow.confirmを使用しましたが、WijmoのPopupコントロールを使うことで、よりリッチなUIを実現することも可能です。Popupコントロールを使用した例は以下のデモアプリケーションでご確認ください。

なお、製品サイトで公開しているデモアプリケーションにもイベントを利用したデータ検証を実装している例がありますので、ご参照ください。

さいごに

今回はWijmoに含まれているFlexGridコントロールでデータ検証を行う方法として、イベントを利用した方法を解説しました。

本連載では、前回のCollectionViewを利用したデータ検証と、今回のイベントを利用したデータ検証の2つの方法をご紹介しましたが、アプリケーションの要件や実装したい検証内容に応じて適した方法が異なります。以下の表にそれぞれの特長をまとめています。

機能CollectionViewを利用した検証イベントを利用した検証
エラー通知UI標準機能として提供アプリ側で実装が必要
型解析エラーの検出標準機能として提供アプリ側で実装が必要
設定済みデータのエラー検出標準機能として提供アプリ側で実装が必要
値の修正標準機能なしアプリ側で実装可能

CollectionViewのデータ検証機能ではエラーを通知するUIが標準で用意されており、少ない実装でエラー表示まで含めた基本的な検証を実現できるのに対し、イベントを利用したデータ検証では、値を自動修正して保存するなど、より自由度の高い機能を実装可能です。要件に応じてこれらの方法を使い分けることで、柔軟かつ実務に適したデータ検証を実現できます。

なお、Wijmoではこの他にも、入力できる値自体を制限してエラーを事前に防ぐための機能も用意されていますので、こちらもぜひあわせてご確認ください。

Wijmoの各種コントロールの基本的な使い方や応用的な使い方の解説を連載記事として公開しています。こちらも是非ご覧ください。

製品サイトでは、Wijmoの機能を手軽に体験できるトライアル版も公開しておりますので、こちらもご確認ください。

また、ご導入前の製品に関するご相談、ご導入後の各種サービスに関するご質問など、お気軽にお問合せください。

\  この記事をシェアする  /