context.AddResponseの用途別実装例
context.AddResponse は、サーバーで判断した結果を、ブラウザで実行する命令として返すために使います。項目設定だけでは表現しにくい案内、複数要素の表示切替、独自UIとの連携、保存エラー時の入力箇所への誘導を実装できます。
ここでは用途ごとに、サーバースクリプトを置く条件、必要な画面設定、コード、確認方法を揃えます。Methodの仕様はMethodと引数の一覧、命令の実行順はdispatchで確認できます。
用途に応じて組み合わせを選ぶ
| やりたいこと | 主なMethod | このページの例 |
|---|---|---|
| 入力状態に応じて案内・領域を切り替える | Remove・After・Toggle | 追加確認の案内とセクション |
| サーバーで計算した値を次の入力に使う | SetValue・SetFormData | 数量×単価の参考額 |
| サーバーのデータを独自UIへ渡す | Invoke | タイトルと判定結果の表示 |
| 保存を止めて修正箇所へ誘導する | Css・Focusとcontext.Error | 追加確認の理由が未入力 |
実例の共通条件
通常の記録テーブルの新規・編集画面を対象にします。例は独立して試せます。必要な項目をエディタへ配置し、その項目の読み取り・更新権限があるアカウントで、検証用レコードを使ってください。セクションのIDはサンプルでは1としています。実際の要素IDに合わせて変更します。
掲載コードを、JavaScriptのDOM検証環境で本体のdispatch・値設定・送信バッファ処理と組み合わせて確認しています。実機でのサーバースクリプト実行、通信、保存、テーマごとの描画は別途確認してください。
命令が反映される時点
AddResponse はその場でブラウザのDOMを変更せず、命令を応答コレクションへ追加します。初回表示ではhidden inputに出力されたJSONをDOM Readyで実行します。自動ポストバックでは、標準のフィールド応答などを生成し、ResponseCollection.ToJson() がサーバースクリプトの命令を末尾へ追加します(追加処理、初回表示、応答への結合、自動ポストバックの応答)。
図を読み込み中…
「画面表示の前」は自動ポストバックのフィールド応答でも実行されます。項目の変更に追従する例では、判定に使う項目の自動ポストバックを有効にしてください。送信先は画面の new・edit アクションにクエリを付けたものなので、サンプルのAction判定は初回表示と項目変更の両方を対象にします(画面表示前フック、送信先)。
1. 追加確認が必要なときだけ案内とセクションを表示する
申請画面で「追加確認が必要」のチェックが入ったときに、タイトルの下へ案内を出し、理由や補足情報のセクションを表示する用途です。ブラウザ用スクリプトを追加せず、サーバーで決めた状態から複数の表示を揃えます。
準備とコード
CheckAを配置し、ラベルを「追加確認が必要」にする。自動ポストバックを有効にする。- タイトルを配置する。
- セクション1を作り、補足項目を配置する。初期生成時に
siteSettings.SectionsのHideで除外しない。 - サーバースクリプトの条件を「画面表示の前」にする。
(function () {
if (context.Controller !== 'items' || context.ReferenceType !== 'Results') return;
if (context.Action !== 'new' && context.Action !== 'edit') return;
const needsReview = model.CheckA === true;
context.AddResponse('Remove', '#ManualReviewHint');
if (needsReview) {
context.AddResponse(
'After',
'#Results_TitleField',
'<p id="ManualReviewHint" role="status">追加確認の理由を入力してください。</p>'
);
}
context.AddResponse('Toggle', '#SectionFields1Container', needsReview ? '1' : '0');
})();Remove を先に送り、必要な場合だけ After で作り直すため、繰り返し自動ポストバックしても案内が増えません。挿入するHTMLは固定文字列です。入力内容などの文字列を表示する場合は、例3の .text() を使う方法へ進んでください。
Toggle の値はbooleanではなく文字列の '1'・'0' です。#SectionFields1Container は見出しを含むセクション全体、#SectionFields1 は中身です。コンテナを表示しても、折りたたまれた中身まで展開する命令ではありません(Toggle、セクションの構造)。
確認と応用
チェックをオフ・オン・オフと変更し、案内が消えること、オンのときだけセクションが出ること、案内が重複しないことを確認します。初回表示でも、保存済みのチェック値に応じた状態になります。
セクションがHTMLへ出力されていなければ Toggle で出現させられません。After の対象もDOMに必要です。存在しない場合は操作元のコントロールへ挿入する代替処理があるため、想定外の場所への表示を避けるにはタイトルの配置を確認してください(Afterの代替処理)。
この表示切替は保存制御や権限制御を追加しません。チェックを外したときに理由の保存も止めたい、必ず理由を入力させたい場合は、保存側の条件判定を別に実装します。例4でその組み合わせを扱います。
2. サーバーで計算した参考額を入力欄と送信バッファへ返す
数量と単価から参考額を算出し、利用者が確認してから保存する用途です。画面を更新するだけでなく、次の通信に使う値も揃えます。
準備とコード
- 数値Aを数量、数値Bを単価、数値Cを参考額として配置する。
- 数値A・Bの自動ポストバックを有効にする。
- この例では数量・単価を0〜1,000,000の整数に限定する。小数の丸め・税計算は含めない。
- サーバースクリプトの条件を「画面表示の前」にする。
(function () {
if (context.Controller !== 'items' || context.ReferenceType !== 'Results') return;
if (context.Action !== 'new' && context.Action !== 'edit') return;
const quantity = model.NumA == null ? null : Number(model.NumA);
const price = model.NumB == null ? null : Number(model.NumB);
const valid = [quantity, price].every(function (value) {
return typeof value === 'number' && Number.isInteger(value) &&
value >= 0 && value <= 1000000;
});
const estimate = valid ? String(quantity * price) : '';
const id = 'Results_NumC';
context.AddResponse('SetValue', '#' + id, estimate);
context.AddResponse('SetFormData', id, estimate);
})();数値の空欄は、Nullableなら null、それ以外では0としてmodelへ渡されます。未入力と0を区別したい場合は数値A・BをNullableにします(数値の取得)。
SetValue はコントロールの値を更新しますが、この命令だけでは送信バッファへ登録しません。SetFormData が応答処理に渡された data の Results_NumC を更新します。初回表示と通常の自動ポストバックでは、ここで扱うのはMainFormのバッファです(2つの命令、SetValue、初回のバッファ、自動ポストバックのバッファ)。
確認と使い分け
数量を3、単価を120にして、数値Cが360になることを確認します。ブラウザのConsoleで次を実行し、画面とバッファの両方を確認できます。
(function () {
const $control = $p.getControl('NumC');
if (!$control || $control.length !== 1) return;
console.log('画面', $control.val());
console.log('送信バッファ', $p.getData($control)[$control.attr('id')]);
})();このコードは model.NumC へ代入せず、DBへ直接保存する処理も呼びません。更新操作と再読み込みで保存値を確認します。正式な金額を必ずこの計算結果で保存したい場合は、保存前の model の制御や計算式を使い、応答を保存値の保証として扱わないでください(保存前のmodel制御)。
Set を使う方法もありますが、通常の単一選択ではchangeを発火します。値設定だけをしたい例では SetValue とバッファ登録を分けています。また SetValue はフィールドの非表示も操作し、Options省略時は hidden クラスを外します。非表示の項目を対象にするなら、第4引数へ JSON.stringify({ Hide: true }) を指定します(値設定の違い、非表示制御)。
3. サーバーの判定結果を独自ブラウザ関数へ渡す
複数の値を渡して、ブラウザ側で案内の文章や表示要素を組み立てたい用途です。サーバーでは結果データを作り、HTML生成はブラウザ側へ分けます。
ブラウザ側のコード
テーブルのスクリプトに置き、新規・編集画面へ出力します。関数はトップレベルで定義し、on_editor_load の中へ入れません。初回の応答がDOM Readyでこの関数を呼ぶためです。
$p.manualRenderReview = function (json) {
const state = JSON.parse(json);
const $field = $p.getField('Title');
if (!$field || $field.length !== 1) return;
let $note = $('#ManualServerReview');
if ($note.length === 0) {
$note = $('<p/>').attr({ id: 'ManualServerReview', role: 'status' });
}
$note.insertAfter($field);
$note.text(state.title + ':' + (state.needsReview ? '追加確認が必要です。' : '通常の確認です。'));
};サーバー側のコード
CheckA とタイトルを配置し、CheckAの自動ポストバックを有効にします。サーバースクリプトの条件を「画面表示の前」にします。
(function () {
if (context.Controller !== 'items' || context.ReferenceType !== 'Results') return;
if (context.Action !== 'new' && context.Action !== 'edit') return;
context.AddResponse('Invoke', 'manualRenderReview', JSON.stringify({
title: model.Title == null ? '' : String(model.Title),
needsReview: model.CheckA === true
}));
})();Invoke のTargetは $p の直接のキーです。manualRenderReview を指定すると $p.manualRenderReview(value) が呼ばれます。関数名に $p. を付けたり、関数そのものをサーバーから送ったりしません(Invokeの実装)。
タイトルが 確認 <例> でも .text() で文字列として表示します。再実行時は同じIDの要素を更新し、現在のフィールドの直後へ移すため、表示が重複しません。CheckAを変更したときに、新しい判定が案内に反映されるか確認してください。
Invoke は、HTML更新命令による $p.apply()・applyValidator() より前に実行されます。上の関数は通常の段落だけを扱う同期処理です。Promiseを返す処理の完了や、UI再適用後の処理を後続命令が待つ構造にはなっていません。再適用後の表示再構築はdispatchの実装サンプルの after_set を使う例を参照してください(実行順)。
4. 保存を止め、理由の入力欄へフォーカスする
「追加確認が必要」がオンなのに理由が空欄なら、サーバーで保存を止め、ブラウザへ戻ったときに理由の入力欄を目立たせてフォーカスします。単にメッセージを返すより、利用者が修正箇所を見つけやすくなります。
準備とコード
- CheckAを「追加確認が必要」、説明Aを「追加確認の理由」として配置する。
- 説明Aは通常の編集可能なtextareaで確認する。
- サーバースクリプトの条件を「更新前」にする。この例は通常の更新リクエストだけを対象にする。
(function () {
if (context.Controller !== 'items' || context.ReferenceType !== 'Results') return;
if (context.Action !== 'update') return;
const reason = model.DescriptionA == null ? '' : String(model.DescriptionA).trim();
if (model.CheckA === true && reason === '') {
context.Error('追加確認が必要な場合は、追加確認の理由を入力してください。');
context.AddResponse('Css', '#Results_DescriptionA', JSON.stringify({
Name: 'outline', Value: '2px solid #b42318'
}));
context.AddResponse('Focus', '#Results_DescriptionA');
} else {
context.AddResponse('Css', '#Results_DescriptionA', JSON.stringify({
Name: 'outline', Value: ''
}));
}
})();保存を止めるのは context.Error です。 Css・Focus はその後の画面案内を担います。記録テーブルの更新処理は、更新前スクリプトの後に context.ErrorData を確認し、エラーがあれば後続の更新処理へ進まず返します(Errorの設定、更新前の中止判定)。
通常の更新応答のエラー分岐は MessageJson を使います。これはContextを持つ ResponseCollection をJSON化するため、メッセージと合わせて AddResponse の命令も追加されます(更新エラーの返却、MessageJson、応答の結合)。
確認と適用範囲
CheckAをオン、理由を空欄にして更新します。エラーメッセージと理由欄の枠・フォーカスを確認し、再読み込みして保存が止まっていることも確認します。次に理由を入力して更新し、更新後の値を確認します。標準の検証で先に止まる場合は、この更新前スクリプトまで進みません(更新時の実行順)。
この例は Action === 'update' に限定しているため、新規作成やAPI更新の検証には適用しません。すべての保存経路で同じ入力規則が必要なら、作成前・更新前の適用条件と保存経路を整理し、ブラウザ向けの命令を送れる経路と分けて実装してください。全体像とmodelの反映条件も確認します。
動かないときに追う順番
- サーバースクリプトの条件、Controller・Action、対象項目の読み取り権限を確認する。
- 初回なら
#ServerScriptResponseCollection、AjaxならNetworkの応答で、Method・Target・Valueが入っているか確認する。 - Targetの要素が今のDOMにあるか、Invokeの関数が
$pに定義済みか確認する。 - CssのValueやOptionsがJSON文字列になっているか、Toggleが
'1'・'0'か確認する。 - 命令順と例外を確認する。例外が起きると後続命令やUI再適用まで進まない。
- 項目の値を変更した場合は、画面・送信バッファ・保存後の値をそれぞれ確認する。
応答はブラウザで順番に実行されます。途中まで変更した画面を元に戻す処理はdispatchにないため、関数の未定義やJSONの不正を先に解消します(命令の走査、Optionsの解析)。