ページサイズ
フルネーム | |
|---|---|
| 記録は見つかりませんでした。 |
MainGrid / SubGridでAllowEdit="true"を設定すると、ギアメニューのオーバーフローに「編集可能」とラベル付けされたトグルが表示されます。オンにすると、セルレンダラーが上書きされていないすべての宣言済みカラムが切り替わります。これはそのカラムのDataverseメタデータから自動送信され、GridTemplateColumnのEditChildContentが読み取りモードChildContentから引き継ぎます。編集はグリッドのトランザクションバッファに保留中の行の変更として蓄積され、ユーザーが保存した場合にのみDataverseに到達します。
contactテーブルの上でAllowEdit="true"をMainGrid。ツールバーのオーバーフローにある歯車アイコンをクリックして、編集可能をオンにしてください。フルネーム欄は、イニシャルアバターリードテンプレートを、ユーザー入力をfirstname+lastnameに分割するカスタムシングルフィールドエディターに置き換えます。シンプルなEmail、Phone、Age、Accountの各列は、それぞれのメタデータタイプに合ったエディタで点灯します(文字列はTextEdit、整数はNumberEdit、顧客参照はLookupEdit)。列ごとの配線は一切ありません。
ページサイズ
フルネーム | |
|---|---|
| 記録は見つかりませんでした。 |
グリッド上に AllowEdit="true" セットしてください。編集はグリッドごとにオプトイン制で、トグルはパラメータが設定された時のみ表示され、トグル自体は少なくとも1列が編集可能な場合にのみ表示されます。トグルがオフの間は、グリッドは読み取り専用ビューとして振る舞います。オンの間、セルはインラインエディタをレンダリングし、保存されていない変更は行ごとに追跡されます。
<MainGrid tableName="contact" allowEdit>
<GridColumns>
<GridColumn columnName="firstname" />
<GridColumn columnName="lastname" />
<GridColumn columnName="emailaddress1" />
</GridColumns>
</MainGrid><MainGrid TableName="contact" AllowEdit="true">
<GridColumns>
<GridColumn ColumnName="firstname" />
<GridColumn ColumnName="lastname" />
<GridColumn ColumnName="emailaddress1" />
</GridColumns>
</MainGrid>なぜトグルなのか?
インライン編集は、見える各行ごとにセルのレンダリングを変更します。これは静的グリッドとは異なる読み取りの許容度です。このトグルにより、必要に応じて(値の入力やセル間のタブ移動など)編集アフォーダンスに切り替え、それ以外の時間は読みやすいアフォーダンスを保持できます。グリッドはセッションのトグル状態を記憶します。
ChildContentなしの裸<GridColumn ColumnName="..." />宣言を出すと、グリッドが編集モードの間に右のインラインエディタを自動発送します。フレームワークは列のメタデータを読み取り、エディターのサブクラスを選択します:
String、 Memo、 EmailAddress、 Phone、 Url — TextEdit (フォーマットに応じた検証付き)。Integer、 Decimal、 Double、 BigInt — NumberEdit (ローカレ認識解析、メタデータからの最小/最大解析)。Boolean — BoolEdit (スイッチとして表示され、中央配置)。Picklist — ChoiceEdit (オプションセット駆動のドロップダウン、ローカライズされたラベル)。MultiSelectPicklist — MultiSelectChoiceEdit。Lookup、 Customer、 Owner — LookupEdit (タイプ先行駆動で、ターゲットテーブルの許可リストを尊重)。DateTime、 DateOnly — DateTimeEdit。Money — MoneyEdit (通貨記号の接頭辞は transactioncurrencyidから)。File、 Image — FileEdit / ImageEdit (ドラッグドロップアップロード)。ChildContentGridColumnに供給して、その列の読み取りモードディスプレイと編集モードエディタの両方を操作します。ChildContent設定が完了すると、フレームワークはメタデータ駆動型エディターの自動ディスパッチをやめ、テンプレート内の任意のエディターをレンダリングし、グリッドのEditableフラグからその可視性をゲートします。標準的なエディタ(TextEdit、NumberEdit、ColumnEdit)はテンプレートに自動でダーティトラッキングとカスケード行コンテキストによる検証を割り当てました。
{/* カスタムディスプレイセルに加えてカスタムエディターも使えます。Reactはこの二つを分けています
cellRenderer(読み取り専用)とeditRenderer(編集モード)に半分 —
<GridTemplateColumn> 両方のレンダラーが共存できるように</GridTemplateColumn>使います。
同じ論理欄。ドロップインされた <TextEdit> 自己レジスタ</TextEdit>
カスケーディング行コンテキストを通じたダーティトラッキングと検証。 */}
<GridTemplateColumn
displayName="メール"
dependsOn={['emailaddress1']}
sortBy="emailaddress1"
cellRenderer={({ record }) => {
const email = (record.properties?.emailaddress1 as { value?: string })?.value;
return <span>{email}</span>;
}}
editRenderer={() => (
<TextEdit columnName="emailaddress1" displayLabelWhenAvailable={false} />
)}
/><!-- カスタムディスプレイセルに加えてカスタムエディターも使えます。一度ChildContentが
提供された場合、フレームワークはもはやインラインを自動ディスパッチしません
このコラムのエディターですので、自分でレンダリングしても構いません
セルは編集可能。ドロップインされたエディタは
カスケーディング行コンテキストを通じたダーティトラッキングと検証。 -->
<GridColumn ColumnName="emailaddress1">
<ChildContent>
@{
var email = context.PrimaryRecord
.GetValueOrDefault<StringValue>("emailaddress1")?.Value;
}
@if (this.Editable)
{
<TextEdit ColumnName="emailaddress1"
DisplayLabelWhenAvailable="false"
DisplayTooltipWhenAvailable="false" />
}
else
{
<span>@email</span>
}
</ChildContent>
</GridColumn>各列は独自の EditContextValidatorを維持しています。標準エディタはマウント時に登録し、エラーをインライン(赤下線+セルのツールチップ)で表示します。未解決の検証エラーがある列の間は、グリッドのセーブボタンが無効になります。検証ルールは列メタデータから得られます:
RequiredLevel — 列が必要であり、空の値がセーブをブロックします。Format 文字列カラムでは、メール、電話、URL形式が型の上に形状を強制します。MinValue / MaxValue 数値列 — 範囲外の値は保存前にフラグが立てられます。EditContextValidator APIを通じてフォームごとに追加ルールを登録し、クロスフィールド不変(例:「連絡先はメールアドレスまたは電話のいずれか)」などを活用します。カスタムウィジェットの検証
ChildContent/EditChildContent内でレンダリングされたカスタム入力でBaseEditを拡張しないもの(例:生のFluentTextFieldやサードパーティのウィジェット)は、行のバリデーターに自動登録されません。もし検証が必要なら、生のウィジェットの代わりに標準エディタを使うか、ウィジェットのValueChangedコールバック内で手動で検証を実行してからeditCtx.SetValueしてください。
編集は行の「保留中の更新」として蓄積されます。修正されたセルには小さな「汚れた」インジケーターが表示され、削除された行は取り消し線、保留中の作成行は新規として表示されます。グリッドのツールバーは、保留変更があるたびに保存とキャンセルボタンを表示します。セーブは一回のトランザクションパスでバッチをコミットします。キャンセルするとバッファを破棄し、各行をサーバー状態に戻します。Buttonsフラグメント内のNewRecordGridButtonとDeleteRecordGridButtonと組み合わせることで、同じ編集トランザクションに行の作成と削除を追加できます。
<MainGrid tableName="contact" allowEdit>
<GridColumns>
<GridColumn columnName="firstname" />
<GridColumn columnName="lastname" />
<GridColumn columnName="emailaddress1" />
</GridColumns>
<GridButtons>
<NewRecordGridButton>
<NewContactForm />
</NewRecordGridButton>
<DeleteRecordGridButton />
</GridButtons>
</MainGrid><MainGrid TableName="contact" AllowEdit="true">
<GridColumns>
<GridColumn ColumnName="firstname" />
<GridColumn ColumnName="lastname" />
<GridColumn ColumnName="emailaddress1" />
</GridColumns>
<Buttons>
<NewRecordGridButton TForm="NewContactForm" />
<DeleteRecordGridButton />
</Buttons>
</MainGrid>GridTemplateColumnの読み取りモードChildContentとEditChildContentを組み合わせれば、合成/合成カラムを編集可能にできます。編集テンプレートにはGridTemplateColumnEditContextが付与されます — 読み取りテンプレートと同じ行コンテキスト(editCtx.Row.PrimaryRecord、SubGridのオプションの editCtx.Row.ParentRecord editCtx.DependsOnMetadata)に加え、命令型editCtx.SetValue(columnName, value)ヘルパーが付随します。
EditChildContent グリッドが編集可能(AllowEdit="true" かつ 編集可能 トグルがオン)で、行が保留・削除されていない場合にのみ発動します。編集モードでも読み取り専用のままのテンプレート列 EditChildContent — 常に同じボタンを描画すべきアクション列に便利です。
最も一般的なケースは、セルが複数のバウンドされた列から構成されていて、ユーザーがそれぞれ編集できるようにしたい場合です。EditChildContentの中にマッチング<TextEdit> / <NumberEdit> / <ColumnEdit>エディタを落とします — フレームワークは行のプライマリレコードとマッチングEditContextValidatorをそのスコープにカスケード化し、エディタが自己登録してダーティトラッキングと検証を行います。追加の配線は必要ありません。
<GridTemplateColumn
displayName="フルネーム"
dependsOn={['firstname', 'lastname']}
sortBy="lastname"
cellRenderer={({ record }) => {
const first = (record.properties?.firstname as { value?: string })?.value ?? '';
const last = (record.properties?.lastname as { value?: string })?.value ?? '';
return <strong>{first} {last}</strong>;
}}
// ドロップインエディター — TextEditはダーティトラッキングに自動配線します
// そしてカスケーディング行コンテキストによる検証。
editRenderer={() => (
<div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
<TextEdit columnName="firstname" displayLabelWhenAvailable={false} />
<TextEdit columnName="lastname" displayLabelWhenAvailable={false} />
</div>
)}
/><GridTemplateColumn Title="フルネーム"
DependsOn="@(new[] { "firstname", "lastname" })"
SortBy="lastname">
<ChildContent Context="ctx">
<strong>
@(ctx.Row.PrimaryRecord.GetValueOrDefault<StringValue>("firstname")?.Value)
@(ctx.Row.PrimaryRecord.GetValueOrDefault<StringValue>("lastname")?.Value)
</strong>
</ChildContent>
<!-- ドロップインエディター — TextEditはダーティトラッキングに自動配線します
そしてカスケーディング行コンテキストによる検証。 -->
<EditChildContent Context="editCtx">
<FluentStack Orientation="Orientation.Horizontal" HorizontalGap="6"
VerticalAlignment="VerticalAlignment.Center">
<TextEdit ColumnName="firstname"
DisplayLabelWhenAvailable="false"
DisplayTooltipWhenAvailable="false" />
<TextEdit ColumnName="lastname"
DisplayLabelWhenAvailable="false"
DisplayTooltipWhenAvailable="false" />
</FluentStack>
</EditChildContent>
</GridTemplateColumn>エディターの表面がDataverseの列に1:1でマッピングされない場合は(単一の「First Last」テキストボックスで2列に解析され、複数のパーセントフィールドに結びついたスライダー、開始+終了を書くカスタム日付範囲ピッカー)、独自の入力をレンダリングし、影響を受けた各列ごとに editCtx.SetValue(columnName, value) を呼び出してください。フレームワークは書き込みを正しい AliasedTableRecord にルーティングし、 ValueChanged を発射するので、ダーティトラッキングで変更を拾います。
<GridTemplateColumn
displayName="フルネーム"
dependsOn={['firstname', 'lastname']}
sortBy="lastname"
cellRenderer={({ record }) => {
const first = (record.properties?.firstname as { value?: string })?.value ?? '';
const last = (record.properties?.lastname as { value?: string })?.value ?? '';
return <strong>{first} {last}</strong>;
}}
editRenderer={({ record, setValue }) => {
const first = (record.properties?.firstname as { value?: string })?.value ?? '';
const last = (record.properties?.lastname as { value?: string })?.value ?? '';
const combined = [first, last].filter(Boolean).join(' ');
// カスタムウィジェット — スペース分割;消費者の見解
// 認証 <Input> 自己認識しない。
return (
<Input
value={combined}
onChange={(_, data) => {
const next = data.value ?? '';
const idx = next.indexOf(' ');
const head = idx === -1 ? next : next.slice(0, idx);
const tail = idx === -1 ? '' : next.slice(idx + 1);
setValue('firstname', { $type: 'StringValue', value: head });
setValue('lastname', { $type: 'StringValue', value: tail || null });
}}
/>
);
}}
/><GridTemplateColumn Title="フルネーム"
DependsOn="@(new[] { "firstname", "lastname" })"
SortBy="lastname">
<ChildContent Context="ctx">
@{
var first = ctx.Row.PrimaryRecord.GetValueOrDefault<StringValue>("firstname")?.Value;
var last = ctx.Row.PrimaryRecord.GetValueOrDefault<StringValue>("lastname")?.Value;
}
<strong>@first @last</strong>
</ChildContent>
<EditChildContent Context="editCtx">
@{
var combined =
(editCtx.Row.PrimaryRecord.GetValueOrDefault<StringValue>("firstname")?.Value ?? "")
+ " "
+ (editCtx.Row.PrimaryRecord.GetValueOrDefault<StringValue>("lastname")?.Value ?? "");
}
<!-- カスタムウィジェット — スペース分割;消費者の見解
FluentTextFieldは自己登録しないため、検証が重要です。 -->
<FluentTextField Value="@combined.Trim()" ValueChanged="@(next =>
{
var parts = next.Split(' ', 2);
editCtx.SetValue("firstname", new StringValue { Value = parts[0] });
editCtx.SetValue("lastname", new StringValue { Value = parts.Length > 1 ? parts[1] : null });
})" />
</EditChildContent>
</GridTemplateColumn>命令型経路での検証
標準的なエディタは
EditChildContent行のEditContextValidatorに自動的にレジスタに入り、フレームワークの保存前検証ゲートに参加します。editCtx.SetValueパスはそのパイプラインをバイパスします。登録された編集者から得られていない値は検証されません。カスタムウィジェットが制約を強制する必要がある場合は、SetValue呼び出す前にValueChangedコールバック内で制約を実行するか、ドロップインエディタパターンに戻してください。