Power Apps で親子関係のある入力フォームを作ってみる
はじめに
親子関係のあるような複雑なデータを編集する場合は、できればモデル駆動型アプリを使っていただくのが正解だとは思うのですが、いろいろな都合により (主にライセンスの関係で) Microsoft Dataverse を使うことができないという場合もあるでしょう。そこで SharePoint リストを使ったキャンバス アプリで親子関係を持つデータをひとつの画面で編集するアプリを作ってみたいと思います。今回は見積書を作成するサンプル アプリを作成します。
実行手順
リストの作成
SharePoint の任意のサイトに以下の 2 つのリストを作成します。
見積書リスト
表示名 | 内部名 | 種類 |
---|---|---|
見積番号 | Title |
1行テキスト |
見積日 | QuotationDate |
日付と時刻 |
会社名 | CompanyName |
1 行テキスト |
納期 | Delivery |
1 行テキスト |
有効期限 | Expiry |
1 行テキスト |
支払条件 | PaymentTerm |
1 行テキスト |
見積書明細リスト
表示名 | 内部名 | 種類 |
---|---|---|
見積書 ID | QuotationId |
数値 |
連番 | Index |
数値 |
摘要 | Title |
1 行テキスト |
数量 | Quantity |
数値 |
単価 | UnitPrice |
数値 |
金額 | Amount |
集計値 |
注意点として、親子関係を定義するのに、参照列を使うのではなくて、ID の値を格納する列を作るようにしています。参照列は Power Apps では委任の関係から逆に扱いづらくなるため、このような方式を採っています。
アプリの作成
一覧画面
一覧画面はあまり本題ではないので詳細は省略します。見積書リストのアイテムの一覧をギャラリーで表示します。
作成画面
上部にフォームで見積書リストのアイテム、下部にギャラリーで見積書明細のリストのアイテムの一覧を表示します。
スクリーンの OnVisible
プロパティでは QuotationDetails
と QuotationDetailsIndexed
という 2 つのコレクションを初期化します。QuotationDetails
はギャラリーにバインドされるデータで、QuotationDetailsIndexed
は保存時に連番を付けるために使用します。
NewForm(CreateForm);
ClearCollect(QuotationDetails, Filter(見積書明細, '見積書 ID' = 0));
ClearCollect(QuotationDetailsIndexed, Filter(見積書明細, '見積書 ID' = 0))
ギャラリーの Items
プロパティにはアイテムが連番で並ぶように以下の数式を設定します。
SortByColumns(Filter(QuotationDetails, 連番 > 0), "Index", Ascending)
ギャラリーのテキスト入力の OnChange
プロパティでは入力したテキストがコレクションに反映されるように Patch
を呼び出すようにします。
Patch(QuotationDetails, ThisItem, { 摘要: Self.Text })
保存ボタンの OnSelect
プロパティでは、まず親の見積書リストのアイテムを SubmitForm
で登録し、次に子の見積書明細リストのアイテムを ForAll
と Patch
を使って登録していきます。ギャラリーは項目が追加されたり削除されたりして連番が続きになっていない可能性があるため、いったん QuotationDetails
のコレクションを QuotationDetailsIndexed
にコピーする形で連番を振り直しています。
If(
SubmitForm(CreateForm),
Clear(QuotationDetailsIndexed);
ForAll(
QuotationDetails,
Collect(
QuotationDetailsIndexed,
{
ID: ThisRecord.ID,
'見積書 ID': CreateForm.LastSubmit.ID,
連番: CountRows(QuotationDetailsIndexed) + 1,
摘要: ThisRecord.摘要,
数量: ThisRecord.数量,
単価: ThisRecord.単価
}
)
);
ForAll(
QuotationDetailsIndexed,
Patch(
見積書明細,
ThisRecord
);
Navigate(
MainScreen,
ScreenTransition.None
)
)
)
更新画面
基本的には作成画面とそれほど変わりません。
スクリーンの OnVisible
プロパティではコレクションを初期化しますが、見積書 ID でアイテムを取ってくるようにします。
EditForm(UpdateForm);
ClearCollect(QuotationDetails, Filter(見積書明細, '見積書 ID' = MainGallery.Selected.ID));
ClearCollect(QuotationDetailsIndexed, Filter(見積書明細, '見積書 ID' = 0))
ギャラリーから項目を削除するときに、新規に追加したものでよければそのままコレクションから削除してしまえばいいのですが、既存のアイテムを削除するときはリストから削除しなければならないので、そのようなアイテムはいったん連番を -1 としておき、保存時にまとめて削除するようにします。削除ボタンの OnSelect
プロパティは以下のようになります。
If(ThisItem.ID = 0, Remove(QuotationDetails, ThisItem), Patch(QuotationDetails, ThisItem, { 連番: -1 }))
保存ボタンの OnSelect
プロパティでは、作成画面とほぼ同じですが、削除とマークされたアイテムを削除するロジックが追加されます。
If(
SubmitForm(UpdateForm),
Clear(QuotationDetailsIndexed);
ForAll(
Filter(
QuotationDetails,
連番 < 0
),
Remove(
見積書明細,
ThisRecord
)
);
ForAll(
Filter(
QuotationDetails,
連番 > 0
),
Collect(
QuotationDetailsIndexed,
{
ID: ThisRecord.ID,
'見積書 ID': UpdateForm.LastSubmit.ID,
連番: CountRows(QuotationDetailsIndexed) + 1,
摘要: ThisRecord.摘要,
数量: ThisRecord.数量,
単価: ThisRecord.単価
}
)
);
ForAll(
QuotationDetailsIndexed,
Patch(
見積書明細,
ThisRecord
);
Navigate(
MainScreen,
ScreenTransition.None
)
)
)
削除ボタンの OnSelect
プロパティでは親子関係のアイテムをすべて削除するようにします。
RemoveIf(見積書明細, '見積書 ID' = MainGallery.Selected.ID);
Remove(見積書, MainGallery.Selected); Navigate(MainScreen, ScreenTransition.None)
実行結果
実行してみます。作成画面で項目を入力して保存をクリックします。
見積書リストにアイテムが追加されます。
同時に見積書明細リストにもアイテムが複数追加されているのがわかります。
明細を編集してみます。マウス を削除して LAN ケーブル を追加します。
見積書明細リストに編集が反映されています。ちゃんと連番もきれいに振られていますね。
おわりに
SharePoint の親子関係の表現には参照列を使いたくなりますが、参照列は ID でのフィルターが委任制約に引っかかるため、使わないのが正解です。件数が増えてきたときのために適切にインデックスを貼っておくとよいと思います。
Discussion
アプリの再現を行おうと試行しているのですが作成画面の見積詳細の追加マークの処理や入力部分をどのように作成しているのかが分からず再現ができない状況です。
細かい手順の解説やアプリのコピーなどいただけたりはしませんでしょうか?