0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

プリザンターのBeforeOpeningPageでgridrowsにフィルターを渡してみる

0
Posted at

はじめに

プリザンターの一覧画面でソートやフィルター変更を行うと、ブラウザは自動的に 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 で書き出します。

Implem.Pleasanter/Libraries/HtmlParts/HtmlScripts.cs(抜粋)
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 まで遅延させるためのラッパーです。

Implem.Pleasanter/Libraries/HtmlParts/HtmlScripts.cs
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 で処理されます。

wwwroot/src/scripts/generals/_dispatch.js(抜粋)
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 が呼ばれます。

Implem.Pleasanter/Libraries/Requests/Views.cs(抜粋)
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 を更新 → 次の gridrows POST ボディに含まれる
  • gridrows 受信後 view.SetByForm が処理してセッションに保存 → 以降の gridrows にも引き継がれる
  • DOM 要素の有無に依存しないため安定して動作する

フィルター UI が DOM に存在する場合は context.ResponseSet("#ViewFilters__カラム名", value) でも同様の効果が得られます。ただし SELECT 要素の場合は auto-postback が発動する可能性があるため SetFormData の方が安全です。

初回描画のフィルター適用が必要なユースケースでは api/sessions との組み合わせも検討してみてください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?