Skip to content

Latest commit

 

History

History
207 lines (141 loc) · 10.9 KB

File metadata and controls

207 lines (141 loc) · 10.9 KB

ModuleField (モジュール)

これは何か

他のモジュールの 1 レコードを画面内に詳細フォームとして埋め込むフィールド。親モジュールの一部のフィールドだけを別モジュールに切り出して、画面上では 1 つのフォームに見せたい時に使います。

埋め込まれた子モジュールは、親モジュールの保存処理と一緒に保存されます (1 トランザクション)。

いつ使うか

  • 「住所」「連絡先」「請求先情報」のように まとまったフィールド群を別モジュールに分けて管理したい
  • 同じ詳細レイアウトを 複数の親モジュールから使い回したい
  • 親レコードと 1:1 で関連するデータを画面内で同時編集したい
  • ダイアログやダッシュボード等で スクリプトで動的にモジュールを差し替えたい (テンポラリ用途)

複数件を扱うなら List / DetailList / TileList、選択だけなら Link を使います。


動作モード

ModuleField は 永続化モードテンポラリモード の 2 つの使い方があります。DbColumn の有無だけでモードが決まります。

モード DbColumn ModuleName / LayoutName 用途
永続化モード 設定 設定 (LayoutName は省略可) 親テーブルに FK 列を持ち、特定の子モジュール 1 件を関連付ける
テンポラリモード 任意 (空でも初期値設定でもOK) スクリプトで SetModule して動的に埋め込み内容を切り替える (保存なし)

DbColumn を設定する場合は ModuleName も必須 です (どのモジュールを参照するか確定しないため)。デザイナのデザインチェックで検出されます。

永続化モードの動き

  • 親テーブルの DbColumn で指定した列に 子モジュールの Id (FK) が保存される
  • 親レコードの読込時、FK 経由で子モジュールのデータも JOIN でまとめて取得される (件数が多くてもクエリは増えない)
  • 親モジュールの Submit ボタンを押すと、親と子が 1 トランザクションで保存される (子側の Add/Update/Delete が親の Submit に乗る)
  • 親レコードが新規 (まだ保存されていない) 場合、子側も新規として一緒に Insert される

テンポラリモードの動き

  • DbColumn を空にして配置 (ModuleName / LayoutName は空でも、初期表示用に設定しておいてもOK)
  • 初期状態は ModuleName 設定値に従って描画される (未設定なら何も表示されない)
  • スクリプトから SetModule("対象モジュール名", "レイアウト名") を呼ぶとその場で子モジュールが差し替わる
  • DB に保存されない (FK 列が無いため)
  • セッション内のみ有効。画面リロードや別ページからの遷移で初期状態に戻る

デザイナでの設定

ModuleFieldのプロパティパネル

プロパティ一覧

システム

C#名 日本語表示名 説明
- フィールドタイプ モジュール 固定

基本設定

C#名 日本語表示名 既定値 説明
Name 名前 string "" フィールド識別子
DbColumn DBカラム string "" 親テーブルの FK 列名 (子モジュールの Id を保存)。テンポラリモードでは空
ModuleName モジュール名 string "" 埋め込む子モジュール名。テンポラリモードでは空
LayoutName レイアウト名 string "" 子モジュールの Detail レイアウト名。空時は既定レイアウト
IsUpdateProtected 更新無効 bool false 更新時に FK 列を変更できないようにする
OnDataChanged データ変更イベント string "" 子モジュールの値が変わった時に発火するスクリプト
IgnoreModification 変更判定から除外 bool false 親モジュールの変更検知 (IsModified) から除外

ModuleField には 表示名 / 必須 の設定はありません (埋め込み先のモジュール側で制御します)。


使い方の流れ

永続化モード

  1. 子モジュール (例: Address) の Detail レイアウトを用意する
  2. 親モジュール (例: Customer) のテーブルに、子モジュールの Id を保存する FK 列 (例: address_id) を追加する
  3. 親モジュールに ModuleField を配置し、DbColumn = "address_id" / ModuleName = "Address" / LayoutName = "" (default) を設定する
  4. 親モジュールの Detail レイアウトに ModuleField を置く
  5. 画面では親と子のフォームが 1 つに見え、Submit ボタンで両方が保存される

テンポラリモード

  1. ModuleField を配置し DbColumn を空にする (ModuleName / LayoutName は空でも、初期表示用に設定しておいてもOK)
  2. 親モジュールの Detail レイアウトに ModuleField を置く
  3. スクリプトで MyField.SetModule("SomeModule", "") を呼ぶと、その場で子モジュールが組み上がって表示される

スクリプトから

プロパティ・メソッド

名前 型・戻り値 説明
ChildModule Module? 埋め込まれた子モジュールのインスタンス。子の各 Field にここからアクセス
ModuleName string 現在埋め込んでいるモジュール名 (SetModule で上書き済なら上書き値、未上書きなら Design 値)
ModuleLayoutName string 現在使っているレイアウト名 (同上)
SetModule(moduleName, layoutName) Task 埋め込むモジュール/レイアウトを動的に変更 (制約あり、後述)

共通プロパティは Field 共通プロパティ を参照。

子モジュールの値にアクセス

// 子モジュールの Field を直接参照
Address.ChildModule.PostalCode.Value = "100-0001";

// 親の Field と組み合わせて使う
void Name_OnDataChanged()
{
    // Customer.Name が変わったら住所欄を初期化
    Address.ChildModule.AddressLine.Value = "";
}

SetModule の制約

SetModule(moduleName, layoutName)DbColumn が空のテンポラリモードでのみ使用可能です。DbColumn を設定した永続化モードの ModuleField で SetModule を呼ぶと例外になります (DB 列の FK と実際の埋め込み内容がズレるとデータ整合性が壊れるため)。

状況 動作
SetModule の引数が現在の ModuleName / ModuleLayoutName と一致 no-op (何もしない。idempotent。ChildModule は再生成されず編集中データも保持)
DbColumn が設定済 例外 (永続化対象の ModuleField は中身を変えられない)
DbColumn が空 かつ 引数が現在と異なる 切替実行 (ChildModule が新しく作り直される)

ModuleName / LayoutName がデザイナで設定済みでも、DbColumn が空ならテンポラリモード扱いで SetModule で上書き可能です (Design 値は「初期表示用の既定」として扱われる)。

よくある例

// テンポラリモードの ModuleField を動的に切り替える
if (UserType.Value == "admin")
{
    await ProfilePanel.SetModule("AdminProfile", "");
}
else
{
    await ProfilePanel.SetModule("UserProfile", "");
}

SetModule でモジュールを切り替えると ChildModule は新しく作り直され、編集中のデータは失われます (同じ引数での再呼出は no-op で残ります)。永続化モード (DbColumn 指定済) では使えません。

OnDataChanged の発火タイミング

OnDataChanged イベントは、子モジュール内のいずれかの Field の値が変わった時に親モジュール側のスクリプトとして発火します。

// Customer.mod.cs (親側)
void Address_OnDataChanged()
{
    // Address (子) のいずれかの Field が変わったら、ここが呼ばれる
    Logger.Log("住所が変更されました");
}

SetModule でモジュールを差し替えただけでは発火しません (モジュール構成変更はデータ変更ではない扱い)。


保存と読込

保存 (Submit)

親モジュールの Submit ボタンが押された時:

  1. 子モジュールに変更があれば、その差分が親の Submit に組み込まれる
  2. 1 トランザクションで親と子が一緒に保存される
  3. 親が新規の場合、子も同時に新規 Insert され、生成された子の Id が親の FK 列に書き込まれる
  4. 親の OnBeforeSubmit などのスクリプトは親側で動く (子側のイベントは子のモジュールで動く)

子モジュールの Submit ボタンを子のレイアウト内に配置する必要はありません。親の Submit に乗ります (置いても害はない)。

読込

親レコードを表示する時:

  • 親モジュールの SELECT で JOIN を使って子モジュールのデータも 1 クエリでまとめて取得される (一覧画面で 100 件の親レコードを表示しても、子モジュール取得用のクエリは増えない)
  • 子モジュールに LinkField / SelectField / 別の ModuleField がネストしていれば、それらも続いて取得される

取得対象は Design.LayoutName で指定した Detail レイアウトに置かれているフィールド だけです。レイアウト外の Field の値が必要な場合はスクリプトで明示的に取得が必要です。


デザイン検証

デザイナで以下のチェックが行われ、エラーがあれば一覧に表示されます。

  • DbColumn が指定されているが対応する DB 列が存在しない
  • ModuleName が存在しないモジュールを指している
  • LayoutNameModuleName のモジュールに存在しないレイアウトを指している
  • OnDataChanged で指定したスクリプト関数が存在しない
  • DbColumn が指定されているのに ModuleName が未設定 (永続化モードのためには両方必須)

関連項目