PowerAppsで親子関係のあるリストを管理する

今回はPowerAppsで親子関係のあるSharePointリストを管理するアプリを構築する方法をご紹介します。
といってもこれは半ば自分自身のための備忘録のようなもので、もし万一どなたかの参考になることがあれば幸いです。

# Problem

PowerAppsでは定義済みのSharePointリストを元に、リストのアイテムを一覧し、詳細表示し、追加・更新・削除するためのアプリケーションをスキャフォールディング（Scaffolding）に構築できます。

例えば次のようなSharePointリストがあった時：
リスト名： `TestObjects`

| 列名（内部名） | 列名（表示名） | データ型 |
|:-----------:|:------------:|:------------:|
| ID       | ID        | 数値         |
| Title     | タイトル      | 文字列       |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/b6908c39-78f3-89ad-ac85-eafa0471643b.png)

PowerApps の画面で、SharePointリストを元にアプリを作成する場合、対象サイト＆リストを選ぶだけで・・・：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/a20c2da7-46ba-4022-93ea-e48ca5b3f26e.png)

あとは自動で 一覧・詳細表示・編集の3画面を持つアプリが自動作成されます：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/c0943688-e9eb-78d0-48d7-8b44a756b619.png)

けれでも、自動作成が対応しているのはここまでです。
例えば先程のリスト `TestObjects` に紐づく次のようなSharePointリストがあったら・・・：

リスト名： `TestSubObjects`

| 列名（内部名） | 列名（表示名） | データ型 |
|:-----------:|:------------:|:------------:|
| ID       | ID        | 数値         |
| Title     | タイトル      | 文字列       |
| SuperObjectId     | SuperObjectId      | 数値       |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/0e8e47c5-256a-7c74-b1dd-dd12958d6715.png)

この子リストと親リストを関連付け、子リストの 一覧・詳細表示・編集の3画面を用意するのはアプリ開発者のお仕事です。

:::note info
ここでは議論をシンプルにするために子リストの `SuperObjectId` は親リストの参照ではなく単なる数値型の列としています。
:::

# Solution

何はともあれ、「データ」を追加します：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/a8b173a7-38ab-24fd-7b13-bcc8abe1a50a.png)

続いてスクリーンを追加します。
今回は次のように2つの画面（Screen）を追加してサブリストの管理をできるようにします。

__＜変更前＞__ 

| スクリーン名 | 役割 | 自動生成 |
|:-----------:|:------------:|:------------:|
| BrowseScreen1       | 親リストのアイテムの一覧、アイテム追加画面への遷移        | ◯         |
| DetailScreen1     | 親リストのアイテムの詳細表示、アイテム編集画面への遷移、アイテムの削除      | ◯       |
| EfitScreen1     | 親リストのアイテムの追加・変更      | ◯       |

__＜変更後＞__

| スクリーン名 | 役割 | 自動生成 |
|:-----------:|:------------:|:------------:|
| BrowseScreen1       | 親リストのアイテムの一覧、アイテム追加画面への遷移        | ◯         |
| DetailScreen1     | 親リストのアイテムの詳細表示、アイテム編集画面への遷移、アイテムの削除、 __子リストのアイテムの一覧、子リストのアイテムの追加画面への遷移__      | ◯       |
| EfitScreen1     | 親リストのアイテムの追加・変更      | ◯       |
| __DetailScreen2__       | __子リストのアイテムの詳細表示、子リストのアイテムの編集画面への遷移、子リストのアイテムの削除__       |          |
| __EfitScreen2__       | __子リストのアイテムの追加・変更__        |          |

## スクリーンの追加

2つのスクリーンを追加して名称を変更します：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/61493c7b-539b-bc8f-94ce-f009ad25f303.png)

`DetailScreen1` のコントロールをごっそり `DetailScreen2` へとコピーします。これらは後ほどプロパティに変更を加えて子リストのアイテムを表示するコントロールにします：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/249f541d-6b86-c1a1-33d3-8a985a0aff7b.png)

`EditScreen1` のコントロールをごっそり `EditScreen2` へとコピーします。これらは後ほどプロパティに変更を加えて子リストのアイテムを編集するコントロールにします：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/a96daa68-5fff-d39f-b999-7a75014d24a5.png)

`BrowseScreen1` のナビゲーションとギャラリーをごっそり `DetailScreen1` へとコピーします。これらは後ほどプロパティに変更を加えて子リストのアイテムを一覧表示するコントロールにします。今回は検索ボックスは対象外にしました：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/55b34fb8-cbd1-1b7c-e720-b308f334da29.png)

## `DetailScreen1` の変更

子リストのアイテムの一覧表示のために先程ごっそりコピーしてきたコントロールたちのプロパティを変更します：

1. ナビゲーションのテキストを `TestObjects` から `TestSubObjects` へ変更、さらに背景色も変更
1. `BrowseGallery1_1` のデータソースを `TestObjects` から `TestSubObjects` へ変更
1. `BrowseGallery1_1.OnSelect` プロパティの式を `Navigate(DetailScreen2, ScreenTransition.None)` へ変更、 `>` クリック時の遷移先を子リストの詳細表示画面に改める
1. `BrowseGallery1_1.Items` プロパティの式を `SortByColumns(Filter([@TestSubObjects], SuperObjectId = BrowseGallery1.Selected.ID), "Title", If(SortDescending1, Descending, Ascending))` へ変更、「現在選択中の親リストのアイテム」の情報をキーにしたフィルタリングを設定
1. `IconRefresh1_1.OnSelect` プロパティの式を `Refresh([@TestSubObjects])` に変更、クリック時にリフレッシュするデータソースを子リストに改める
1. `IconNewItem1_1.OnSelect` プロパティの式を `NewForm(EditForm1_1);Navigate(EditScreen2, ScreenTransition.None)` に変更、クリック時の遷移先を子リストの編集画面に改める

#4の変更イメージ：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/81c9e87c-605f-60b6-e835-47d46436bfc1.png)

## `DetailScreen2` の変更

子リストのアイテムの詳細表示のため先程ごっこりコピーしてきたコントロールたちのプロパティを変更します：

1. ナビゲーションのテキストを `TestObjects` から `TestSubObjects` へ変更、さらに背景色も変更
1. `DetailForm1_1` のデータソースを `TestObjects` から `TestSubObjects` へ変更
1. `DetailForm1_1.Item` プロパティの式を `BrowseGallery1_1.Selected` へ変更、表示対象を改める
1. `IconDelete1_1.OnSelect` プロパティの式を `Remove([@TestSubObjects], BrowseGallery1_1.Selected); If (IsEmpty(Errors([@TestSubObjects], BrowseGallery1_1.Selected)), Back())` へ変更、削除対象を改める
1. `IconEdit1_1.OnSelect` プロパティの式を `EditForm(EditForm1_1);Navigate(EditScreen2, ScreenTransition.None)` に変更、遷移先を改める
1. `IconBackarrow1_1.OnSelect` プロパティの式を `Navigate(DetailScreen1, ScreenTransition.None)` に変更、遷移先を改める

#2と#3の変更イメージ：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/1d71c757-4ea8-759e-e3ed-fc93d2eb7519.png)

## `EditScreen2` の変更

子リストのアイテムの編集のため先程ごっこりコピーしてきたコントロールたちのプロパティを変更します：

1. ナビゲーションのテキストを `TestObjects` から `TestSubObjects` へ変更、さらに背景色も変更
1. `EditForm1_1` のデータソースを `TestObjects` から `TestSubObjects` へ変更
1. `EditForm1_1.Item` プロパティの式を `BrowseGallery1_1.Selected` へ変更、入力欄に表示する対象を改める
1. `DataCardValue5.Default` プロパティの式を `BrowseGallery1.Selected.ID` へ変更、`BrowseScreen1` で選択された親リストのアイテムのIDが自動で設定されるように改める
1. `DataCardValue5.DisplayMode` プロパティの式を `DisplayMode.View` へ変更、ユーザーによる書き換えを禁じる

#2と#3の変更イメージ：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/c16d32da-37a1-2ef5-b027-b897f1e10840.png)

#5の変更イメージ：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/193b42b5-1cbf-7544-3d3a-cb1341ff7963.png)

## まとめ

これで子リストの管理もできるアプリケーションになりました。
子リストの一覧表示のため `Filter()` 関数の引数の変更や、親リストのアイテムの `ID` を自動設定＆読み取り専用にする変更のほかは、基本的にコピー＆ペーストで作成したコントロール群の参照先を変更するだけでした。

もちろんかなり荒削りなので、実用化のためには見た目も動きもいろいろ変更しないといけないでしょう。
各種コントロールもコピーによって自動採番された名前をそのまま使っているので、保守性のためにはこれらも改めたほうがよいでしょう。