からめもぶろぐ。

俺たちは雰囲気で OAuth をやっている

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 では委任の関係から逆に扱いづらくなるため、このような方式を採っています。

アプリの作成

一覧画面

f:id:karamem0:20201118092912p:plain

一覧画面はあまり本題ではないので詳細は省略します。見積書リストのアイテムの一覧をギャラリーで表示します。

作成画面

f:id:karamem0:20201118092949p:plain

上部にフォームで見積書リストのアイテム、下部にギャラリーで見積書明細のリストのアイテムの一覧を表示します。

スクリーンの 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
        )
    )
)

更新画面

f:id:karamem0:20201118093121p:plain

基本的には作成画面とそれほど変わりません。

スクリーンの 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)

実行

実行してみます。作成画面で項目を入力して保存をクリックします。

f:id:karamem0:20201118095810p:plain

見積書リストにアイテムが追加されます。

f:id:karamem0:20201118095834p:plain

同時に見積書明細リストにもアイテムが複数追加されているのがわかります。

f:id:karamem0:20201118095924p:plain

明細を編集してみます。マウスを削除して LAN ケーブルを追加します。

f:id:karamem0:20201118100125p:plain

見積書明細リストに編集が反映されています。ちゃんと連番もきれいに振られていますね。

f:id:karamem0:20201118100213p:plain

まとめ

SharePoint の親子関係の表現には参照列を使いたくなりますが、参照列は ID でのフィルターが委任制約に引っかかるため、使わないのが正解です。件数が増えてきたときのために適切にインデックスを貼っておくとよいと思います。