context.Condition・Action・QueryStrings
サーバースクリプトの 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() が渡されています。
Context = new ServerScriptModelContext(
context: context,
// ...(中略)...
controlId: context.Forms.ControlId(),
condition: condition.ToString());condition は ServerScriptModel のコンストラクタ引数で、型は ServerScriptConditions です。
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 呼び出しの落とし穴 を参照)。
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 部分を取り出して小文字にしたものです。
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 |
| BinariesUpload | binaries/upload | Controller = 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 で確認)。
var mode = context.QueryStrings.Data('mode');取得可否の早見表
値が入る前提は、HTTP リクエストの URL にクエリパラメータが含まれていることです。
| 呼び出し方法 | 例 | context.Action 例 | context.QueryStrings |
|---|---|---|---|
| ブラウザの画面遷移(新規・編集・詳細・一覧、URL にクエリ付き) | /items/123/edit?mode=review | index / new / edit | 取得できる |
| コマンドボタンからの API 呼び出し(Cookie セッションあり) | /api/items/123/update?action=approve | update など | 取得できる |
| スクリプトからの API 呼び出し(Cookie セッションあり) | $.ajax('/api/items/123/update?flag=1') | update など | 取得できる |
| 一覧のソート・フィルタ変更 | /items/123/gridrows | gridrows | 取得できない(空文字列) |
| API キー認証での API 呼び出し | リクエストボディで ApiKey を指定 | update など | 取得できない(空文字列) |
ソート・フィルタ変更(gridrows)では取得できない
外部システムから /items/123?mode=review のような URL で一覧画面を開いた場合、初期表示(index)では値を取得できますが、その後ソートやフィルタを操作すると取得できなくなります。
| ステップ | 操作 | リクエスト URL | context.QueryStrings.Data('mode') |
|---|---|---|---|
| 1 | 外部システムのリンクをクリック | /items/123?mode=review | "review" |
| 2 | 一覧でソートを変更 | /items/123/gridrows | "" |
| 3 | フィルタを変更 | /items/123/gridrows | "" |
| 4 | ページを再読み込み | /items/123?mode=review | "review" |
ページを再読み込みしない限り、一度ソートやフィルタを操作すると以降は参照できません。
// gridrows リクエスト時
context.Action; // "gridrows"
context.QueryStrings.Data('mode'); // "" (常に空文字列)
context.QueryStrings.Data('myFlag'); // "" (常に空文字列)URL にクエリパラメータが付かない理由
ソート変更時、フロントエンドは次の手順でリクエスト URL を生成します。
- ソートヘッダーに設定された
data-action="GridRows"を読み取る form.actionの_action_をgridrowsに置き換えて URL を決定する
var url = action !== undefined
? $p.store.$form.attr('action').replace('_action_', action.toLowerCase())
: location.href;
// → /items/123/gridrows(クエリパラメータなし)form.action はサーバー側で /items/{サイトID}/_action_ として生成されます。元のページ URL に ?mode=review が付いていても、フォームの action 属性には引き継がれません。フィルタ変更も同様に data-action="GridRows" が設定された要素から URL を生成するため、同じ結果になります。
図を読み込み中…
API キー認証の呼び出しでは取得できない
API リクエストのボディで ApiKey を指定して認証する場合、context.QueryStrings.Data() は空文字列になります。mode など特定のキーだけでなく、クエリで渡した任意のキーで同様です。この経路では、クエリパラメータがサーバースクリプトの context.QueryStrings に取り込まれません。
{
"ApiVersion": 1.1,
"ApiKey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}取得できない場合の代替案
gridrows で操作内容を読む:context.Forms
ソートやフィルタの内容はクエリではなく POST ボディ(フォームデータ)として送信されるため、context.Forms で参照できます。
// どのフィルタ・ソートが変更されたか
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 で読めます。
// 画面表示の前(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 へ値を設定すると、ソート・フィルタ変更後も条件がセッションに保持されます。ただし、パラメータの値がフィルタの有効な値でない場合は使えません。
// 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 ボディに制御値を入れます。認証方式の違いに影響されにくくなります。
{
"ApiVersion": 1.1,
"ClassA": "review"
}// 例: ClassA に入れた値で分岐する
// (リクエストボディの ClassA が model.ClassA に反映される)
if (model.ClassA === 'review') {
// レビュー向け処理
}その他
ワークフローや運用ルールとして残したい制御値は、クエリではなく**テーブル項目(明示的なフラグ列)**として持つ方が安全です。監査・再現性・保守性の観点でも有利です。
画面入力と連動する値は、クエリではなく
context.Formsから取得する方が意図が明確です。jsvar copyWithComments = context.Forms.Bool('CopyWithComments');
使い分けの指針
| 用途 | 使うもの |
|---|---|
| 一時的な処理切り替え(ブラウザ経由・初期表示) | context.QueryStrings |
ソート・フィルタ変更(gridrows)で URL パラメータを引き継ぎたい | 初期表示で SetFormData、gridrows で context.Forms |
ソート・フィルタ変更(gridrows)で操作内容を読む | context.Forms |
| API キー認証が絡む経路 | リクエストボディ |
| 恒久的な業務データ | テーブル項目 |
| 画面入力データ | context.Forms |
最初に「どの経路で実行されるサーバースクリプトか」を決めてから、受け渡し方法を選んでください。
関連ページ
- 操作別に見るサーバースクリプトと拡張 SQL の実行順
- context.AddResponse の Method と引数
- 拡張サーバースクリプトとバックグラウンドサーバースクリプト
- httpClient と外部 API 呼び出しの落とし穴
- view.OnSelectingWhere とフィルター —
BeforeOpeningPageからgridrowsにフィルターを渡す方法