Skip to content

Lookup の転記と上書き制御 ​

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

Lookup(ルックアップ)は、リンク項目で選んだレコードから、現在のレコードの別項目へ値を転記する機能です。候補を絞る項目連携や ColumnFilterExpressions とは処理の目的が違います。

転記の可否は、保存済みの値だけでなく、送信したフォームに転記先の値が含まれるか、どの項目が通信を起こしたかによっても変わります。 Overwrite と OverwriteForm は、別の判定に使われます。

公式マニュアルとの関係 ​

設定方法・対応する項目・オプションの基本は、公式のルックアップを参照してください。このページでは、記録テーブルの SetByLookups と Lookups.LookupData を中心に、転記判定と画面反映を説明します。確認は本体ソースによるもので、実機の画面操作は検証していません。

商品の単価を転記する構成例 ​

商品マスタをサイト ID 200 とし、編集中のテーブルでは ClassB に商品、NumA に単価を設定する例です。サイト ID はダミーです。商品マスタ側にも単価の NumA があるものとします。

ClassB の選択肢一覧に、次の構成を設定します。

json
[
  {
    "SiteId": 200,
    "Lookups": [
      {
        "From": "NumA",
        "To": "NumA",
        "Type": 0,
        "OverwriteForm": true
      }
    ]
  }
]

From は参照先の列、To は現在のテーブルの列です。Type は 0 が値、1 が表示名です(Lookup の設定項目)。

編集直後に単価を画面へ反映する構成では、商品を選ぶ ClassB の自動ポストバックを有効にします。返却する項目を限定している場合は、転記先の NumA も含めます。Lookup の設定自体は、変更イベントを登録する処理ではありません(自動ポストバックの送信、返却対象)。

転記はサーバーのモデルで行う ​

フォームを使う記録テーブルのモデル生成では、フォーム値の反映後に SetByLookups を呼ぶ経路があります。SetByLookups は、Lookup 設定があり、リンク項目が更新対象になっているか、要求のフォームにリンク項目のキーがあるものを対象にします(モデル生成、SetByLookups)。

Lookup の対象は、設定のあるすべてのリンクではありません。リンク項目が更新対象か、フォームにそのキーがあるかも確認します。

csharp
.Where(link => link.Lookups?.Any() == true)
.Where(link => PropertyUpdated(
    context: context,
    name: link.ColumnName)
        || context.Forms.ContainsKey($"{ss.ReferenceType}_{link.ColumnName}"))

引用元のコード(3296–3300 行)

PropertyUpdated と ContainsKey は OR 条件です。保存済みのリンク先だけを変わらず参照していても、フォームの要求にリンク項目が含まれれば対象になります。逆に Lookup 設定があるという理由だけで、すべての要求で全リンクの転記が行われるわけではありません。

処理は、リンク項目の値を参照先の ID として読み、LookupData で転記する値の辞書を作り、その辞書を SetByForm で現在のモデルに反映します。_dispatch.js が参照先の単価を取得しているわけではありません。

図を読み込み中…

Lookup はサーバーのモデル処理であり、自動ポストバック専用のブラウザ機能ではありません。画面へ即時反映する通信と、値を決める処理を分けて考えます。

Overwrite と OverwriteForm の判定 ​

LookupData は、転記先ごとに次の二つの条件を両方満たすかを判定します(転記対象の絞り込み)。

二つの上書き条件は、一つの OR 式ではなく、連続する Where です。最初の条件を通った転記先だけが、次の条件へ進みます。

csharp
.Where(lookup => lookup.Overwrite != false
    || blankColumns.Contains(lookup.To)
    || formData.Get($"{ss.ReferenceType}_{lookup.To}") == string.Empty)
.Where(lookup => (lookup.OverwriteForm == true
    && formData.Get("ControlId") == $"{ss.ReferenceType}_{link.ColumnName}")
        || formData?.ContainsKey($"{ss.ReferenceType}_{lookup.To}") != true
        || (isNewRecord && IsDefaultValue(
            context: context,
            ss: ss,
            formData: formData,
            lookup: lookup)))
.ToList();

引用元のコード(63–74 行)

OverwriteForm=true は、発火元がこのリンク項目であることと組で判定されます。また、Overwrite=false の条件を迂回する設定ではありません。例えば保存済みの NumA が空ではなく、要求の Results_NumA も空文字でない場合、最初の Where で除外されるので、二つ目を満たしても転記されません。一方、転記先がフォームにない場合は二つ目の条件を通れますが、最初の上書き可否の条件は引き続き必要です。

判定転記を許可する条件
Overwrite 側Overwrite が false ではない、または転記先が空値判定のリストに含まれる、または送信された転記先が空文字
OverwriteForm 側OverwriteForm=true で発火元の ControlId がそのリンク項目、またはフォームに転記先のキーがない、または新規レコードで転記先の送信値が既定値と判定される

Overwrite は未指定なら false として扱われません。OverwriteForm は未指定でも、転記先がフォームに含まれない場合などには転記を許可します。「OverwriteForm がないと Lookup が一切動かない」という判定ではありません。

手入力した転記先との関係 ​

例えば、単価 NumA を手入力して送信バッファに格納したあと、商品 ClassB を変更した場合です。

設定と条件判定
Overwrite 未指定、OverwriteForm 未指定、フォームに非既定値の NumA があるフォーム値の判定で転記対象から外れる
Overwrite 未指定、OverwriteForm=true、発火元が ClassB商品からの転記がフォーム値より優先される
Overwrite=false、OverwriteForm=true、保存済み NumA は非空で、送信値も非空Overwrite 側の条件で転記対象から外れる

OverwriteForm=true にしても、Overwrite=false の判定を無条件に無視するわけではありません。また、発火元が別の項目なら OverwriteForm=true の発火元条件を満たしません。

空値と既定値は別の判定 ​

保存済みレコードでは、SetByLookups が保存済みの転記先の値を Column.BlankValue で判定します。未保存のモデルでは現在の値を使います。空値の扱いは型によって違い、例えば空を許可しない数値では 0、チェックでは false も空値側になります(空値リストの生成、BlankValue)。

新規レコードのフォーム値については、別に IsDefaultValue が判定します。設定された既定値との一致、既定値がない場合の空文字、空を許可しない数値の 0、チェックの false などを扱います(IsDefaultValue)。

画面へ返すときにバッファも処理する ​

自動ポストバックの FieldResponse は、LookupClearFormData を呼びます。この処理は、発火元のリンクのうち OverwriteForm=true の転記先へ ClearFormData を返します。その後、返却対象の転記先を値や HTML として返します(FieldResponse の先頭、LookupClearFormData)。

画面への応答では、発火元のリンクと OverwriteForm の設定から、バッファを削除する命令を作ります。

csharp
ss.Links
    ?.Where(link => $"{ss.ReferenceType}_{link.ColumnName}" == context.Forms.ControlId())
    .Where(link => link.Lookups != null)
    .SelectMany(link => link.Lookups)
    .Where(lookup => lookup.OverwriteForm == true)
    .ForEach(lookup => res.ClearFormData($"{ss.ReferenceType}_{lookup.To}"));

引用元のコード(15–20 行)

ここには「実際に Lookup の転記が成功したか」の結果を読む条件がありません。対象のキーが削除されたことだけでは、転記成功の証拠にはなりません。応答の ClearFormData と、値や HTML を返す命令を別々に確認します。

応答役割
ClearFormData送信バッファにある転記先の値を削除
ReplaceAllLookup の転記先など、差し替え対象のフィールドを再描画
SetValueフィールドを差し替えない場合に表示値を設定
Invoke("initRelatingColumnEditorNoSend")項目連携の属性を送信なしで再設定

発火元のリンクの Lookups の転記先は、ReplaceFieldColumns に加わります。ただし、返却対象を絞る設定があれば、その対象に含まれる項目だけが反映されます(差し替え対象)。

ClearFormData は画面の入力値を空にする命令ではありません。_dispatch.js が $p.clearData を呼び、送信データのキーを削除します。また、LookupClearFormData は転記が実際に行われたかを再判定せず、発火元と OverwriteForm の設定で命令を生成します(命令の実行、バッファの削除)。

転記されない・空になるときの確認点 ​

  • リンク項目が対象か — SetByLookups の対象になる更新状態、フォーム内のリンク項目のキーを確認します。
  • 転記先を手入力していないか — フォームに転記先のキーがある場合は、OverwriteForm と発火元を確認します。
  • 上書きを禁止していないか — Overwrite=false と空値判定を確認します。
  • 参照先を読めるか — 通常のテーブルではレコードの参照権限を確認し、転記元の列についても読取権限を判定します(レコードの判定、転記元の列の判定)。
  • 転記先が返却対象か — 自動ポストバック時の返却対象と、応答の ReplaceAll・SetValue を確認します。

転記対象となった列の辞書は、まず空文字で作られます。参照 ID が正数で、参照先の設定があり、読み取りが許可される場合に取得値で置き換えます。リンクの解除などで取得条件を満たさない場合、転記対象の値は空文字のまま返されます。「取得できなければ必ず元の値が残る」という動作ではありません(LookupData)。

Lookup は先に転記対象の辞書を空文字で作り、参照先を読める場合に取得値へ置き換えます。

csharp
var changedFormData = lookups.ToDictionary(
    lookup => $"{ss.ReferenceType}_{lookup.To}",
    lookup => string.Empty);
if (id > 0
    && currentSs != null
    && canRead)

引用元のコード(75–80 行)

リンクを解除して ID が正数でなくなる場合も、上書き条件を通った転記先のキーは辞書にあります。その値は空文字のままです。「参照できないので何もしない」と「空文字を転記する」は同じではありません。値が消えたときは参照 ID と上書き条件を両方調べます。

複数のリンクから同じ転記先へ値を返す場合は、SetByLookups がリンクを ControlledOrder に従って並べ、同じ転記先のキーでは先頭の結果を採用します。設定の競合も確認してください(転記結果の集約)。

関連ページ ​

変更履歴

第2版コードを引用しながらイベント・項目連携・送信処理の解説を詳しくする
第1版画面項目の連携・送信と非同期処理の解説を拡充し検索向け情報を整備