Skip to content

context.Condition・Action・QueryStrings ​

第7版作成 最終更新 (日本時間)
確認バージョン1.5.7.01.5.8.1

サーバースクリプトの context.Condition には「どの条件で実行されたか」、context.Action には「どの画面・操作から実行されたか」が入ります。公式マニュアルには取り得る値が載っていないため、このページで一覧にします。2 つを組み合わせると、実行条件をかなり細かく分けられます。

あわせて、URL のクエリパラメータを読む context.QueryStrings が呼び出し経路によっては空になる条件と、その場合の代替案もまとめます。

context.Condition ​

取り得る値 ​

context.Condition の値は、列挙体 ServerScriptConditions の列挙子名を ToString() した文字列です。サーバースクリプトの「条件」との対応は次のとおりです。

Condition条件
None(なし)
WhenViewProcessingビュー処理時
WhenloadingSiteSettingsサイト設定の読み込み時
BeforeOpeningPage画面表示の前
BeforeOpeningRow行表示の前
WhenloadingRecord「レコード」読み込み時
BeforeFormula計算式の前
AfterFormula計算式の後
AfterUpdate更新後
BeforeUpdate更新前
AfterCreate作成後
BeforeCreate作成前
AfterDelete削除後
AfterBulkDelete一括削除後(1.5.8.1 のソースで確認)
BeforeDelete削除前
BeforeBulkDelete一括削除前(1.5.8.1 のソースで確認)
BackgroundServerScriptバックグラウンドサーバースクリプト

WhenloadingSiteSettings と WhenloadingRecord は小文字の l で始まる点に注意してください(列挙子名そのままです)。

値の出どころ ​

context の実装は Implem.Pleasanter.Libraries.ServerScripts.ServerScriptModelContext にあり、Condition プロパティはコンストラクタで設定されます。

このコンストラクタは ServerScriptModel から呼ばれ、最後の引数で condition.ToString() が渡されています。

csharp
Context = new ServerScriptModelContext(
    context: context,
    // ...(中略)...
    controlId: context.Forms.ControlId(),
    condition: condition.ToString());

condition は ServerScriptModel のコンストラクタ引数で、型は ServerScriptConditions です。

csharp
public enum ServerScriptConditions
{
    None,
    WhenViewProcessing,
    WhenloadingSiteSettings,
    BeforeOpeningPage,
    BeforeOpeningRow,
    WhenloadingRecord,
    BeforeFormula,
    AfterFormula,
    AfterUpdate,
    BeforeUpdate,
    AfterCreate,
    BeforeCreate,
    AfterDelete,
    AfterBulkDelete,
    BeforeDelete,
    BeforeBulkDelete,
    BackgroundServerScript
}

1.5.8.1 では、列挙子に AfterBulkDelete(一括削除後)と BeforeBulkDelete(一括削除前)が加わっています(ServerScriptModel.cs(1.5.8.1))。上の表とコードは 1.5.8.1 に合わせています。

使用例 ​

同じスクリプトを「更新前」と「更新後」の両方で動かし、context.Condition で処理を分ける例です(context.UserData で更新前の値を更新後に渡しています。詳しくは httpClient と外部 API 呼び出しの落とし穴 を参照)。

js
if (context.Condition === 'BeforeUpdate') {
  context.UserData.statusBefore = String(saved.Status == null ? '' : saved.Status);
  return;
}

// AfterUpdate
var before = context.UserData.statusBefore;
var after = String(model.Status == null ? '' : model.Status);
if (before !== after) {
  // 保存が完了してから通知する
}

context.Action ​

代表的な値 ​

Action説明
analy分析チャート画面
burndownバーンダウンチャート画面
calendarカレンダー画面
copyコピーが行われたとき
copyrow一覧画面編集で行をコピーしたとき
crosstabクロス集計画面
delete削除が行われたとき
deletecommentコメントが削除されたとき
deletehistory履歴が削除されたとき
edit編集画面
exportエクスポートされたとき
ganttガントチャート画面
gridrows一覧画面で追加行が読み込まれたとき
histories履歴一覧画面
history履歴画面
imagelib画像ライブラリ画面
importインポートされたとき
index一覧画面
kambanカンバン画面
loginログイン画面
new新規作成
newongrid一覧画面編集で新規行を追加したとき
timeseries時系列チャート画面
trashboxゴミ箱画面
update更新処理が実施されたとき

値の出どころ:URL のルート ​

context.Action は、リクエスト URL のルート情報から action 部分を取り出して小文字にしたものです。

csharp
Action = RouteData.Get("action")?.ToLower() ?? string.Empty;

ルートは Startup.cs で定義されています(Startup.cs#L304-L395)。主なパターンは次のとおりです。

ルート名pattern既定値
Default{controller}/{action}Controller = Items, Action = Index
Others{reference}/{id}/{controller}/{action}Action = Index(Controller は Binaries、PublishBinaries、OutgoingMails)
Item{controller}/{id}/{action}Controller = Items, Action = Edit
Binaries{controller}/{guid}/{action}Controller = Binaries
BinariesUploadbinaries/uploadController = Binaries, Action = Upload

たとえば編集画面の URL が /items/3578784/edit なら、Item パターン({controller}/{id}/{action})に一致し、{action} にあたる edit が context.Action になります。API の呼び出しも同じ方法で URL から特定できます。context.Controller も出どころは同じなので、同じ方法で特定できます。

表にない値を調べるには

ソースコードの Implem.Pleasanter\Controllers と上記のルート定義を、ブラウザの開発者ツールで見えるリクエストと突き合わせると、自動ポストバックなどでどの URL が呼ばれているか(= どの Action になるか)が分かります。

context.QueryStrings ​

context.QueryStrings を使うと、URL のクエリパラメータで処理を切り替えられます。ただし呼び出し経路と認証方式によって、値を取得できないケースがあります(1.5.7.0 で確認)。

js
var mode = context.QueryStrings.Data('mode');

取得可否の早見表 ​

値が入る前提は、HTTP リクエストの URL にクエリパラメータが含まれていることです。

呼び出し方法例context.Action 例context.QueryStrings
ブラウザの画面遷移(新規・編集・詳細・一覧、URL にクエリ付き)/items/123/edit?mode=reviewindex / new / edit取得できる
コマンドボタンからの API 呼び出し(Cookie セッションあり)/api/items/123/update?action=approveupdate など取得できる
スクリプトからの API 呼び出し(Cookie セッションあり)$.ajax('/api/items/123/update?flag=1')update など取得できる
一覧のソート・フィルタ変更/items/123/gridrowsgridrows取得できない(空文字列)
API キー認証での API 呼び出しリクエストボディで ApiKey を指定update など取得できない(空文字列)

ソート・フィルタ変更(gridrows)では取得できない ​

外部システムから /items/123?mode=review のような URL で一覧画面を開いた場合、初期表示(index)では値を取得できますが、その後ソートやフィルタを操作すると取得できなくなります。

ステップ操作リクエスト URLcontext.QueryStrings.Data('mode')
1外部システムのリンクをクリック/items/123?mode=review"review"
2一覧でソートを変更/items/123/gridrows""
3フィルタを変更/items/123/gridrows""
4ページを再読み込み/items/123?mode=review"review"

ページを再読み込みしない限り、一度ソートやフィルタを操作すると以降は参照できません。

js
// gridrows リクエスト時
context.Action;                          // "gridrows"
context.QueryStrings.Data('mode');       // "" (常に空文字列)
context.QueryStrings.Data('myFlag');     // "" (常に空文字列)

URL にクエリパラメータが付かない理由 ​

ソート変更時、フロントエンドは次の手順でリクエスト URL を生成します。

  1. ソートヘッダーに設定された data-action="GridRows" を読み取る
  2. form.action の _action_ を gridrows に置き換えて URL を決定する
js
var url = action !== undefined
    ? $p.store.$form.attr('action').replace('_action_', action.toLowerCase())
    : location.href;
// → /items/123/gridrows(クエリパラメータなし)

ソース(_form.js L52-L69)

form.action はサーバー側で /items/{サイトID}/_action_ として生成されます。元のページ URL に ?mode=review が付いていても、フォームの action 属性には引き継がれません。フィルタ変更も同様に data-action="GridRows" が設定された要素から URL を生成するため、同じ結果になります。

図を読み込み中…

API キー認証の呼び出しでは取得できない ​

API リクエストのボディで ApiKey を指定して認証する場合、context.QueryStrings.Data() は空文字列になります。mode など特定のキーだけでなく、クエリで渡した任意のキーで同様です。この経路では、クエリパラメータがサーバースクリプトの context.QueryStrings に取り込まれません。

json
{
  "ApiVersion": 1.1,
  "ApiKey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}

取得できない場合の代替案 ​

gridrows で操作内容を読む:context.Forms ​

ソートやフィルタの内容はクエリではなく POST ボディ(フォームデータ)として送信されるため、context.Forms で参照できます。

js
// どのフィルタ・ソートが変更されたか
var controlId = context.Forms.Data('ControlId');
// 例: "ViewFilters__Status"(フィルタ)/ "Grid"(ソート)

// 特定フィルタの現在値
var statusFilter = context.Forms.Data('ViewFilters__Status');

// 現在のソート列
var sorter = context.Forms.Data('ViewSorters__CompletionTime');
ControlId の値操作
"Grid"ソート変更
"ViewFilters__〇〇"フィルタ変更
"ViewFilters_Reset"フィルタリセット
"ViewSorters_Reset"ソートリセット

URL で渡した値を gridrows 以降でも使う:SetFormData ​

URL で渡された値を gridrows 以降でも使いたい場合は、初期表示(index)の「画面表示の前」(BeforeOpeningPage)サーバースクリプトで、その値を SetFormData でブラウザ側の送信バッファ($p.data.MainForm)に入れておきます。以降の gridrows などの POST ボディにその値が含まれるため、context.Forms で読めます。

js
// 画面表示の前(index): URL パラメータを送信バッファに入れる
if (context.Action === 'index') {
    var mode = context.QueryStrings.Data('mode');
    if (mode !== '') {
        context.AddResponse('SetFormData', 'mode', mode);
    }
}

// index 以外(gridrows など): POST ボディで送られてきた値を参照
if (context.Action !== 'index') {
    var mode = context.Forms.Data('mode');
    // mode を使って処理を分岐
}
  • SetFormData はブラウザで $p.data の該当フォームにキーと値を書き込むだけの処理で(_dispatch.js)、$p.send() は送信のたびにこの $p.data をそのまま POST ボディにします(_form.js)。一覧のソート・フィルタも MainForm から送信されるため、値は gridrows に毎回含まれます。
  • 値はそのタブの画面のメモリに保持されるため、同じユーザーが複数タブで開いても互いに影響しません。ページを再読み込みすると消え、URL にパラメータがあれば再びセットされます。
  • 仕組みの詳細は view.OnSelectingWhere とフィルター の「BeforeOpeningPage から gridrows にフィルターを渡す」を参照してください。

context.UserData はリクエストをまたいで保持されない

context.UserData は ExpandoObject で、リクエストごとに作られる Context のプロパティです(Context.cs、ServerScriptModel.cs)。1 つのリクエストの中で実行されるサーバースクリプト同士(「更新前」と「更新後」など)で値を渡すことはできますが、index でセットした値を後の gridrows リクエストで読むことはできません。また、context.UserData.statusBefore = ... のようにプロパティとして読み書きするもので、Set() / Data() メソッドはありません。値を後のリクエストに引き継ぐには、次の例のように SetFormData を使います。

URL パラメータをビューのフィルタに変換する:view.Filters ​

index 時に view.Filters へ値を設定すると、ソート・フィルタ変更後も条件がセッションに保持されます。ただし、パラメータの値がフィルタの有効な値でない場合は使えません。

js
// index 時の処理: URLパラメータをビューフィルタとして引き継ぐ
if (context.Action === 'index') {
    var mode = context.QueryStrings.Data('mode');
    if (mode !== '') {
        view.Filters.ClassA = mode;  // フィルタとして保持
    }
}

BeforeOpeningPage から gridrows のフォームデータにフィルターを注入する方法は view.OnSelectingWhere とフィルター を参照してください。

API キー認証の経路:リクエストボディに制御値を持たせる ​

context.QueryStrings が使えない経路では、クエリではなく JSON ボディに制御値を入れます。認証方式の違いに影響されにくくなります。

json
{
  "ApiVersion": 1.1,
  "ClassA": "review"
}
js
// 例: ClassA に入れた値で分岐する
// (リクエストボディの ClassA が model.ClassA に反映される)
if (model.ClassA === 'review') {
    // レビュー向け処理
}

その他 ​

  • ワークフローや運用ルールとして残したい制御値は、クエリではなく**テーブル項目(明示的なフラグ列)**として持つ方が安全です。監査・再現性・保守性の観点でも有利です。

  • 画面入力と連動する値は、クエリではなく context.Forms から取得する方が意図が明確です。

    js
    var copyWithComments = context.Forms.Bool('CopyWithComments');

使い分けの指針 ​

用途使うもの
一時的な処理切り替え(ブラウザ経由・初期表示)context.QueryStrings
ソート・フィルタ変更(gridrows)で URL パラメータを引き継ぎたい初期表示で SetFormData、gridrows で context.Forms
ソート・フィルタ変更(gridrows)で操作内容を読むcontext.Forms
API キー認証が絡む経路リクエストボディ
恒久的な業務データテーブル項目
画面入力データcontext.Forms

最初に「どの経路で実行されるサーバースクリプトか」を決めてから、受け渡し方法を選んでください。

関連ページ ​

変更履歴

第7版操作別の実行順と拡張SQLの併用・同期実行の注意点を追加
第6版本文から元記事や以前の版への言及を除き、正しい動作だけを書く形に整理
第5版ボタングループ化の CSS を 1.5.8.1 のラジオボタン・チェックボックスの構造に合わせて書き直し
第4版「サーバースクリプト」を 1.5.8.1 のソースで検証して修正
第3版記事のファイル名に並び順の番号を付け、元記事リンクを frontmatter の sources に移行
第2版サーバースクリプトに view.OnSelectingWhere とフィルターの解説、context.QueryStrings を追加
第1版「サーバースクリプト」セクションの記事を追加