プリザンターの一覧画面の「ヘッダメニューでフィルタ」を可視化する方法

## はじめに
プリザンターのフィルタ機能にある「一覧のヘッダメニューでフィルタを使用する」モード、フィルタ項目を変更しなくても、ヘッダからダイレクトにフィルタがかけられるため非常に便利な機能です。
ただ、フィルタ欄に表示されていない項目に対してフィルタを設定すると、該当項目に対してフィルタがかかっているかどうかが分からないという欠点があります。今回はこの欠点を解消する方法を紹介します。

## 要素をみてみる
「一覧のヘッダメニューでフィルタを使用する」をONにしたテーブルのヘッダー要素の実装を見てみましょう。

```html
<th
    data-name="ClassA"
    class="sortable">
        <div
            data-id="ViewSorters__ClassA"
            data-order-type="asc"
            data-action="GridRows"
            data-method="post">
                <span>親テーブル</span>
        </div>
</th>
```

次にOFFの場合のヘッダー要素の実装を見てみます。
```html:
<th
    data-name="ClassA"
    class="sortable">
        <div
            data-id="ViewSorters__ClassA"
            data-order-type="asc"
            data-action="GridRows"
            data-method="post">
                <span>親テーブル</span>
        </div>
</th>
```

おっと、差違がないですね。他の要素を探してみてもまさにコレといったものがないので、別の方法を考えてみます。

## サーバスクリプトの`View`を使用する
サーバスクリプトでビューを設定する時に使用する`View`オブジェクトですが、実はビュー情報を取得する時にも使用できます。ちょっと中身を見てみましょう。次のようなサーバスクリプトを作ってみて、画面表示の前で表示させてみます。

```js
context.Log($ps.JSON.stringify(view.Filters));
```

ここで、`ClassA`にだけフィルタをかけてみます。デバッグコンソールには次の様な結果が表示されます。

```json
{"ClassA":"[\"3528414\"]"}
```

まさに`ClassA`にだけフィルタがかかっているという情報が取得出来ました。これは使えそうですね。

## ヘッダーにどのように表示するか考えてみる
ソート時に昇順降順を示す△マークが表示されるので、それを参考にしてみます。プリザンターにはGoogle Iconsが組み込まれているので、そのアイコンを使用できます。

![filter_alt_24dp_000000_FILL0_wght400_GRAD0_opsz24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3555395/02eaae0f-6db9-45d4-92b9-70814bbf9796.png)
これが使えそうですね。`filter_alt`になります。

では実際の要素を見てみて、実装出来そうなところを探してみます。
```html
<!--ソートアイコンが表示されている-->
<th data-name="TitleBody" class="sortable">
    <div data-id="ViewSorters__TitleBody" data-order-type="desc" data-action="GridRows" data-method="post">
        <span>タイトル/内容</span>
        <span class="ui-icon ui-icon-triangle-1-n"></span><!--ソートアイコン-->
        <span></span>
    </div>
</th>

<!--ソートアイコンが表示されていない-->
<th data-name="ClassA" class="sortable">
    <div data-id="ViewSorters__ClassA" data-order-type="asc" data-action="GridRows" data-method="post">
        <span>親テーブル</span>
    </div>
</th>
```
タイトルが格納されている要素の直前に追加するのが良さそうですね。`th[data-name="{要素名}"] div`にjQueryの`prepend`メソッドで追加するのが良さそうです。
試しに、追加するだけのスクリプトを書いて見ました。

```js
$('th div').prepend('<span class="material-symbols-outlined">filter_alt</span>');
```
これをデバッグコンソールで実行してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3555395/e7e43a1b-1ff3-4517-8bc7-dbc804d4be26.png)
いい感じに表示されました。

## サーバスクリプトで実装してみる
では実装をサーバスクリプトで行っていきます。`context.AddResponse`の出番です。コードを書いてみると次の様になります。

```js
if (['index', 'gridrows', 'newongrid', 'copyrow'].includes(context.Action)) 
{
	for (const [key, value] of Object.entries(view.Filters)) {
        if (value && value !== '[]') {
    		context.AddResponse('Prepend', `th[data-name="${key}"] div:not(:has(.view-filter-icon))`,
    			'<span class="view-filter-icon material-symbols-outlined" style="font-size: 1.5em;">filter_alt</span>');
        }
	}
}
```
実行されるときの条件を`['index','gridrows', 'newongrid', 'copyrow'].includes(context.Action)`[^修正]に限定することで、ブラウザの画面遷移のタイミングと一覧の表が書き換えられるタイミング、一覧画面編集での行追加と行コピーのタイミング[^修正]に限定しています。また、条件をクリアしたタイミングで選択し項目や日付項目では空であることを明示するために空配列が渡されるため、それも除外しています。

これを拡張サーバスクリプトやスクリプトに「画面表示の前」の条件で設定します。実行してみるとこのようになります。
![レコーディング-2025-03-25-204646.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3555395/c02bcc87-c7bc-46b7-a164-76b915f2b074.gif)
いい感じの挙動ですね。

## まとめ

プリザンターのヘッダーでのフィルタで、今どの項目にフィルタが設定されているかをわかりやすく表示することが出来ました。項目に対して直感的な表示となるため非常にオススメのカスタマイズです。皆さんも是非試してみてください。

[^修正]:一覧画面編集モード時の`Action`指定に漏れがあると指摘をいただき修正しました。