はじめに
プリザンターの一覧画面でソートやフィルター変更を行うと、ブラウザは自動的に action=gridrows の AJAX リクエストを発行します。このリクエストに独自のパラメーターを渡したいケースがあります。たとえば「別のページから遷移してきた際に URL パラメーターを使って特定の絞り込みを事前セットしておきたい」というシナリオです。
しかし、gridrows リクエストの中身はプリザンターが自動生成するフォームデータであり、外から自由にキーを追加できません。本記事では、BeforeOpeningPage(ページを開く前)サーバスクリプトで ResponseCollection に SetFormData 指示を積むことで、ブラウザのフォームデータに ViewFilters__カラム名 を注入し、gridrows 経由でフィルターを引き継ぐ方法を解説します。
バージョン 1.5.9.0 を対象にしています
仕組みの概要
全体の流れを先に整理します。
① URL アクセス(例: /items/123?Dept=sales)
└─ IssueUtilities.Index
├─ Views.GetBySession ← セッションからビューを復元
├─ GetGridData ← 初回 HTML 描画(まだフィルターなし)
└─ ss.GetServerScriptModelRow
└─ BeforeOpeningPage スクリプト実行
└─ context.AddResponse("SetFormData", "ViewFilters__ClassA", '["sales"]')
→ context.ResponseCollection に指示を積む
② HTML レンダリング(Scripts > OnEditorLoad)
→ <input id="ServerScriptResponseCollection" value="[{...SetFormData...}]">
→ $p.setByJson(...) 呼び出しスクリプトを埋め込む
③ ブラウザでページ読み込み
→ $p.setByJson 実行
└─ SetFormData 処理
→ $p.data["MainForm"]["ViewFilters__ClassA"] = '["sales"]'
④ ユーザーがソート/フィルターを操作(gridrows 発行)
└─ POST ボディに ViewFilters__ClassA: '["sales"]' が含まれる
└─ Views.GetBySession
├─ view.SetByForm ← AddColumnFilterHash("ClassA", '["sales"]')
└─ SetSession ← フィルターをセッションに保存
⑤ 以降の gridrows
└─ セッションから ClassA フィルターを復元して適用
ポイントは③で $p.data を書き換えている点です。$p.data["MainForm"] はプリザンターが AJAX リクエスト時に POST ボディとして送るフォームデータの実体であり、ここに ViewFilters__ClassA を入れておくと次の gridrows 送信時に自動的に含まれます。
サンプルコード
シナリオ
一覧画面に ?Dept=sales という URL パラメーターを付けてアクセスした際に、ClassA が「sales」のレコードだけを表示するフィルターを自動適用する。
BeforeOpeningPage サーバスクリプト
// BeforeOpeningPage(ページを開く前)タイミングで設定
// URL クエリストリングからパラメーターを取得
const dept = context.QueryStrings.Data("Dept");
if (dept) {
// $p.data["MainForm"]["ViewFilters__ClassA"] に値をセット
// 次回以降の gridrows POST ボディに含まれるようになる
context.AddResponse(
"SetFormData",
"ViewFilters__ClassA",
JSON.stringify([dept])
);
}
JSON.stringify([dept]) は '["sales"]' という文字列になります。プリザンターのドロップダウンフィルターは値を JSON 配列形式で扱うため、この形式に揃えます。
テキスト検索フィルターに渡す場合
DescriptionA のような自由記述テキストのフィルターには、配列ではなく文字列をそのまま渡します。
const keyword = context.QueryStrings.Data("Keyword");
if (keyword) {
context.AddResponse(
"SetFormData",
"ViewFilters__DescriptionA",
keyword
);
}
ResponseSet を使う方法
context.ResponseSet を使うと、DOM の hidden 入力値も同時に更新できます。
const dept = context.QueryStrings.Data("Dept");
if (dept) {
// '#ViewFilters__ClassA' という CSS セレクターで hidden 要素を特定する
context.ResponseSet(
"#ViewFilters__ClassA",
JSON.stringify([dept])
);
}
ResponseSet は内部で AddResponse("Set", ...) を呼びます。JavaScript 側では $p.set が実行され、対象要素の .val() を更新したうえで $p.setData によって $p.data を更新します。
SetFormData との違いを整理すると次のとおりです。
| 方法 |
$p.data の更新 |
DOM の更新 | DOM 要素が必要か |
|---|---|---|---|
AddResponse("SetFormData", ...) |
される | されない | 不要 |
ResponseSet("#ViewFilters__...", ...) |
される | される | 必要 |
ビューのフィルター設定にそのカラムが含まれていれば #ViewFilters__ClassA という hidden 入力が DOM に存在するため ResponseSet も使えます。一方、フィルター設定に含まれていないカラムを書き込む場合や、確実に動かしたい場合は AddResponse("SetFormData", ...) が安全です。
ResponseSet で SELECT 要素(ドロップダウンフィルター)をターゲットにした場合、auto-postback クラスが付いていると即座に gridrows リクエストが発動する場合があります。フィルターパネルの SELECT 要素を対象にする際は注意してください。
処理の詳細
BeforeOpeningPage が context.ResponseCollection にデータを積むと、HtmlScripts.OnEditorLoad がそれを ServerScriptResponseCollection という hidden フィールドに JSON で書き出します。
private static HtmlBuilder OnEditorLoad(this HtmlBuilder hb, Context context)
{
if (context?.ResponseCollection?.Any() == true)
{
hb
.Hidden(
controlId: "ServerScriptResponseCollection",
value: context.ResponseCollection.ToJson())
.Script(
script: OnDomReadyScript("$p.setByJson(undefined, undefined, $p.getData($('#MainForm')), $('#MainForm'), undefined, JSON.parse($('#ServerScriptResponseCollection').val()));"),
nonce: context.Nonce);
}
...
}
OnDomReadyScript は、埋め込むスクリプトを DOMContentLoaded まで遅延させるためのラッパーです。
private static string OnDomReadyScript(string script)
{
return $"(function() {{ var run = () => {{ {script} }}; if (document.readyState !== 'loading') {{ run(); }} else {{ document.addEventListener('DOMContentLoaded', run); }} }})();";
}
これは非 Ajax のページ描画時(つまり初回アクセス時)に限り実行されます。$p.setByJson が呼ばれると、各 JSON 要素が $p.setByJsonElement で処理されます。
case 'SetFormData':
data[target] = value; // data = $p.getData($('#MainForm'))
break;
data は $p.getData($('#MainForm')) の戻り値で、$p.data["MainForm"] への参照です。このオブジェクトはブラウザの AJAX 送信時に POST ボディの元になるため、ここに ViewFilters__ClassA を追加しておくだけで、次の gridrows に自動的に含まれます。
gridrows を受け取ったサーバー側では Views.GetBySession が呼ばれます。
view = GetView(context, ss, useUsersView); // セッションからビューを復元
view.SetByForm(context, ss); // POST フォームデータを適用
SetSession(context, ss, view, setSession); // セッションに保存
view.SetByForm 内で ViewFilters__ClassA を検出すると AddColumnFilterHash("ClassA", value) が呼ばれてフィルターハッシュに値が追加され、その状態がセッションに保存されます。以降の gridrows はセッション復元時から ClassA フィルターを保持します。
注意事項
初回レンダリングにはフィルターが反映されない
BeforeOpeningPage の SetFormData は $p.data をセットするだけです。最初の HTML 描画(GetGridData の呼び出し時)はこの値を参照しないため、初回表示は未フィルターの状態になります。フィルターが有効になるのは、ユーザーがソートやフィルターを操作した際の初回 gridrows 以降です。
初回から反映したい場合
初回描画にもフィルターを効かせるには、api/sessions/Set でページ遷移前にセッションに値を保存し、WhenViewProcessing タイミングの拡張 SQL で参照する方法が有効です。
セッションに残ることに注意
一度フィルターがセッションに保存されると、ページを開き直しても残り続けます。フィルターを特定の URL パラメーター付きアクセス時だけに限定したい場合は、BeforeOpeningPage でパラメーターが存在しないときに明示的にフィルターを消去する処理も合わせて実装してください。
まとめ
action=gridrows へ任意のフィルターを渡す方法として、BeforeOpeningPage サーバスクリプトで context.AddResponse("SetFormData", "ViewFilters__カラム名", value) を使う手法を紹介しました。
-
SetFormDataでブラウザの$p.dataを更新 → 次のgridrowsPOST ボディに含まれる -
gridrows受信後view.SetByFormが処理してセッションに保存 → 以降のgridrowsにも引き継がれる - DOM 要素の有無に依存しないため安定して動作する
フィルター UI が DOM に存在する場合は context.ResponseSet("#ViewFilters__カラム名", value) でも同様の効果が得られます。ただし SELECT 要素の場合は auto-postback が発動する可能性があるため SetFormData の方が安全です。
初回描画のフィルター適用が必要なユースケースでは api/sessions との組み合わせも検討してみてください。