Skip to content

model の内部構造と反映条件 ​

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

model は実行時にレコードモデルから組み立てる動的オブジェクトです。項目値の読み書きだけでなく、行表示の設定や ReadOnly などを受け取ります。実行条件によって対象レコードの有無が変わり、代入した値にも権限と保存処理の段階があります。

公式マニュアルの model にはプロパティ、使用例、アクセス権による制限が載っています。このページでは、同じ model が実行条件によって何を指すか、代入した値がどの段階まで反映されるか、公式の表だけでは判定できない境界を扱います。

値の作られ方 ​

実行時に ServerScriptUtilities.Values() がレコードから名前と値の組を作り、ServerScriptModel の ExpandoObject に格納します。Title や Status などの標準項目、ClassA・NumA・DateA・DescriptionA・CheckA・AttachmentsA といった拡張項目、Comments が対象です。課題と結果では、Status など一部の標準項目の取り出し方が分かれます(Values())。

受け皿の Model は辞書として値を入れる ExpandoObject です。項目ごとの専用 JavaScript クラスを作っているわけではありません。

csharp
public readonly ExpandoObject Model = new ExpandoObject();
data?.ForEach(datam => ((IDictionary<string, object>)Model)[datam.Name] = datam.Value);

ServerScriptModel.cs の宣言、コンストラクタ の抜粋です。

値の読み出しには項目の CanRead 判定が入り、読めない項目は null として渡されます。サイト設定の ColumnHash に無い名前も null です。ReadOnly だけはこの判定を通さずに入ります(ReadNameValue())。model に名前が見えても、必ず値を取得できるとは限りません。

項目渡される値の特徴根拠
NumA など空欄は Nullable 項目なら null、それ以外は 0数値の取得
DateA など通常は日付値。計算式用の呼び出しではクライアント時刻の文字列日付の取得
AttachmentsA など・CommentsJSON 文字列添付ファイルとコメント
Status・Manager・Owner課題・結果それぞれのモデルから取得する。Manager(管理者)・Owner(担当者)はユーザー ID課題と結果
js
if (model.Status === 900) {
  context.Log(model.Title);
}

実行条件と対象レコード ​

「行表示の前」は一覧の対象レコードを itemModel: this として渡します。一方、「画面表示の前」の実行メソッドは 2 つあり、BaseModel の実装は itemModel: null、課題・結果の親クラス BaseItemModel の override は itemModel: this を渡します(BaseModel、BaseItemModel)。一覧の画面表示前は、SiteSettings.GetServerScriptModelRow() が itemModel なしで呼ばれ、BaseModel を継承した ItemModel の実装、つまり itemModel: null の側を通ります(GetServerScriptModelRow())。同じ条件名だけを見て、model.Status に値があると決めることはできません。

Execute() は itemModel が null なら空の BaseItemModel を作ります。その後 Values() で model の値を組み立てます(Execute())。したがって「画面表示の前」に model 自体が存在しても、その model が一覧の各行のレコードとは限りません。

csharp
itemModel = itemModel ?? new BaseItemModel();

空の BaseItemModel は IssueModel・ResultModel ではないので、Status・Manager などの名前そのものが model に作られません(undefined になる)。行の呼び出し元は this を渡します(行)。

呼び出し元渡される itemModelmodel で扱う対象
一覧の画面表示前null空の基底モデル。Status などの名前も無い
一覧の行表示前行から作った IssueModel / ResultModel現在描画中のレコード
エディタの画面表示前編集対象の IssueModel / ResultModel編集中のレコード
サイト設定読み込み時null実レコードを伴わない

一覧の呼び出し順は、画面表示前が IssueUtilities.Index()、行表示前が HtmlGrids.cs にあります。エディタの呼び出しでは編集対象を渡します(Editor()。自動ポストバックで項目を描き直す EditorFields() も同じ)。

一覧のレコード値で分岐するなら

一覧の各行の Status や ClassA を使う処理は「行表示の前」に置きます。「画面表示の前」は画面共通の処理に使い、レコード値が必要なら呼び出し元を確認します。

項目値を書き換えたとき ​

model は変更されたプロパティ名を記録します。実行後、その名前からサイト設定の項目を探し、編集可能と判定された項目だけを実際のレコードモデルへ反映します(変更検知、変更名の取得、反映対象の絞り込み)。

csharp
private void DataPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    ChangeItemNames.Add(e.PropertyName);
}

これは変更名を記録する箇所です。記録した名前を編集可能な Column に絞ってから値を戻します。

js
model.ClassA = '確認済み';

model に任意の名前を追加すること自体はできますが、その名前が保存対象になるわけではありません。ExtendedRowCss・ExtendedRowData は通常の項目値とは別に読み出されます。

図を読み込み中…

拡張項目は BaseItemModel.SetValue() へ、課題・結果の標準項目は専用の setter へ戻されます。model.ReadOnly は項目の変更名とは別に反映されます(SetValues())。通常の代入はメモリ上のレコードモデルへの反映であり、直ちに DB 更新を意味しません。UpdateOnExit が真なら課題・結果の処理が Update() を呼びます(課題、結果)。

同じ代入でも保存に進む条件が違う ​

条件代入後の処理DB への反映
作成前Create() がサーバースクリプトを実行した後に CreateStatements() を組み立てる通常の作成処理に含まれる
更新前Update() がサーバースクリプトを実行した後に更新処理を進める通常の更新処理に含まれる
行表示の前一覧の行を描くためにレコードモデルへ反映代入だけでは DB 更新しない
UpdateOnExit = true課題・結果の SetValues() が Update() を呼ぶ追加の更新処理が走る

作成前・更新前の実行位置は IssueModel.Create() と IssueModel.Update() で確認できます。どちらもスクリプトを保存処理より先に実行するので、その後の通常の保存処理が変更済みモデルを使います。

js
// 条件: 更新前
if (model.Status === 900) {
  model.ClassA = '完了';
}

この例は更新処理の途中でレコードモデルの ClassA を変えます。ClassA が更新可能な項目なら、後続の更新処理にその値が渡ります。行表示の前に同じコードを書いた場合は、一覧の描画に使うモデルの変更にとどまります。

同じ値を代入しても反映先は項目名で決まる ​

拡張項目は SetExtendedColumnValues() が model.SetValue() に渡し、標準項目は SetIssueModelValues() / SetResultModelValues() が項目ごとに変換して戻します。例えば Status は整数化、Manager と Owner はユーザー ID から SiteInfo.User() を引き直し、日付項目は日付用の変換を通ります(課題の setter)。JavaScript 側で任意の型を入れればその型のまま保存される、という構造ではありません。

値が見えない・変わらないときの確認順 ​

症状ソースで確認する箇所
一覧の画面表示前で model.Status が期待した行の値にならないitemModel: null から空の基底モデルを作る経路。行表示前へ処理を移す
項目名はあるのに値が nullValues() の CanRead 判定
代入後の画面や保存値が変わらない変更名が ColumnHash にあり、CanEdit を通るか
表示時に代入したが DB が更新されないレコードモデルへの反映と Update() 呼び出しの違い
行全体が編集できなくなったmodel.ReadOnly と columns.項目名.ReadOnly の取り違え

レコード全体の ReadOnly と項目の ReadOnly ​

model.ReadOnly はレコードモデル全体の ReadOnly に戻ります。一覧で項目を編集できる状態で描くか(一覧の編集モード)は、行全体の条件(Locked・ReadOnly)を見たあとで、項目の column.GetReadOnly() を見て決めます(一覧の編集分岐)。特定項目だけを読取専用にするなら columns.ClassA.ReadOnly を使います。

行全体に効く値 ​

書く値一覧での反映先用途
model.ExtendedRowCss行の <tr> の class行全体に CSS を当てる
model.ExtendedRowData行の <tr> の data-extension行にデータを渡す

どちらもスクリプト終了後に ServerScriptModelRow に取り出され、一覧の行を組み立てる処理で使われます(SetRow()、HtmlGrids.cs)。一覧の各レコードで「行表示の前」のスクリプトが実行されるため、行ごとに条件を変えられます。

js
// 条件: 行表示の前
if (model.Status === 900) {
  model.ExtendedRowCss = 'completed-row';
}
css
.grid tr.completed-row td {
  background-color: #e8f5e9;
}

ExtendedRowCss は CSS のクラス名を渡します。特定の項目だけを変える場合は、columns の ExtendedCellCss を使います。

関連ページ ​

変更履歴

第1版サーバースクリプトの model が実行条件ごとに何を指し、代入がどこまで反映されるかのページを追加