ギャラリー コントロール完全に理解した！Power Appsでアコーディオンメニューを作成する！！

# はじめに
*Power Appsでアコーディオン メニューを作りたい*
そう思ったことはございませんか？私はあります。

`SharePoint`ではサクっと作成できる**アコーディオン メニュー**、`グループ化されたビュー`という表現もできますが、`Power Apps`でもサクっと作成したい。

今回は、ダミーの従業員リストを用いて
- **部署別**
- **年月別**

上記の**グループ化されたビュー**を作成していきたいと思います。

活用するコントロールは、下記の2点の**ギャラリー コントロール**です。
 - [高さが伸縮可能な空のギャラリー](https://learn.microsoft.com/ja-jp/power-apps/maker/canvas-apps/gallery-dynamic-sizing)
 - [空の垂直ギャラリー](https://learn.microsoft.com/ja-jp/power-apps/maker/canvas-apps/northwind-orders-canvas-part3)

モダン コントロールは、含めない内容にしています。

https://learn.microsoft.com/ja-jp/power-apps/maker/canvas-apps/gallery-dynamic-sizing

https://learn.microsoft.com/ja-jp/power-apps/maker/canvas-apps/northwind-orders-canvas-part3

:::note info
記事の執筆は2024.08.11段階です
:::

# 完成系のイメージ

今回作成する完成系は下記の動画のものです。

<blockquote class="twitter-tweet" data-media-max-width="560"><p lang="ja" dir="ltr">納期からの現実逃避でアコーディオンメニュー作っている・・・ <a href="https://twitter.com/hashtag/PowerApps?src=hash&amp;ref_src=twsrc%5Etfw">#PowerApps</a> <a href="https://t.co/Np6MmKPNI6">pic.twitter.com/Np6MmKPNI6</a></p>&mdash; 出戻りガツオ🐟 Microsoft MVP (@DemodoriGatsuo) <a href="https://twitter.com/DemodoriGatsuo/status/1821197729277866191?ref_src=twsrc%5Etfw">August 7, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## データソース(SharePoint Lists)
私が`SharePoint Lists`で用意している`従業員リスト`を使います。
こちらのデータは全て架空の内容です。

**■ SharePoint Listsの詳細**

| 表示名| 英語の列名 | 列のデータ型 |
|---|---|---|
| タイトル | Title | 1行テキスト |
| Photo | PhotoThumbnail | 縮小表示 |
| Name | Name | 1行テキスト |
| 氏名 | FullName | 1行テキスト |
| フリガナ | PhoneticName | 1行テキスト |
| 誕生日 | Birthday | 日付と時刻 |
| 年齢 | Age | 数値 |
| メールアドレス | EmailAddress | 1行テキスト |
| 携帯電話番号 | MobilePhoneNumber | 1行テキスト |
| 役職 | JobTitle | 1行テキスト |
| セクション | Section | 1行テキスト |
| HashTags | HashTags | 複数行テキスト |
| 性別 | Gender | 選択肢 |
| 部署 | Department | 選択肢 |

この中でも用いる列は
 - **Photo** 
 - **氏名**
 - **誕生日**
 - **携帯電話番号**
 - **部署**

こちらを使用します。

# ステップバイステップで作成してみる
## 1. コレクションの設定

 - `親ギャラリー コントロール`
 - `子ギャラリー コントロール`

双方のデータソースに`コレクション`を設定します。
データの大本は`SharePoint Lists`ですが、キャッシュの仕組みを活用したデータ読み込みの効率化を狙い、`コレクション`にデータを格納して参照していきます。

従業員リストすべてのデータに該当するのは`colChild`という名称を付けたコレクションです。

```javascript:従業員リストすべてのデータ
ClearCollect(
    colChild,
    ShowColumns(
        'employee list',
        Title,
        Photo,
        Name,
        氏名,
        部署,
        役職,
        携帯電話番号,
        誕生日
    )
);
```

https://learn.microsoft.com/ja-jp/power-platform/power-fx/reference/function-clear-collect-clearcollect


コレクションには必要列のみを格納しています。

次に、`親のギャラリー コントロール`に設定するコレクションを設定しましょう。
`部署別`、`年月別`※(誕生日列を使用)を用いて、コレクションを設定します。

まずは`部署別`です。

**■ 部署別**

こちらの例では、部署を表示する`Department`列の重複を[Distinct 関数](https://learn.microsoft.com/ja-jp/power-platform/power-fx/reference/function-distinct)で省き、子のギャラリー コントロールの`visible`プロパティを操作するため、`VisibleStatus`という`Boolean`型の列を[AddColumns 関数](https://learn.microsoft.com/ja-jp/power-platform/power-fx/reference/function-table-shaping)で追加しています。


```javascript:部署別にグループ化するためのコレクション
ClearCollect(
    colDepartment,
    AddColumns(
        Distinct(
            colChild,
            Department
        ),
        VisibleStatus,
        true
    )
)
```

https://learn.microsoft.com/ja-jp/power-platform/power-fx/reference/function-distinct

https://learn.microsoft.com/ja-jp/power-platform/power-fx/reference/function-table-shaping

次に`年月別`を見ていきます。

**■ 年月別※(誕生日列を使用)**

年月別の例は誕生日を表示する`Birthday`列を使っています。

::: note info
あまり誕生日でやるケースはないと思いますが、例としてみてください。
:::

`部署別`とやっていることは似ていますが、日付型の列である`Birthday`列を`yyyy/㎜`の形式の表記に変換し、重複削除したうえで、[Sort 関数](https://learn.microsoft.com/ja-jp/power-platform/power-fx/reference/function-sort)で降順に並び替えています。

```javascript:年月別にグループ化するためのコレクション
ClearCollect(
    colYearMonth,
    AddColumns(
        Distinct(
            Sort(
                colChild,
                Birthday,
                SortOrder.Descending
            ),
            Text(
                Birthday,
                "yyyy/mm"
            )
        ),
        VisibleStatus,
        true
    )
)
```

https://learn.microsoft.com/ja-jp/power-platform/power-fx/reference/function-text

https://learn.microsoft.com/ja-jp/power-platform/power-fx/reference/function-sort

これらを`ギャラリー コントロール`が位置する画面の`OnVisible`プロパティに設定します。

```javascript:OnVisible
ClearCollect(
    colChild,
    ShowColumns(
        'employee list',
        Title,
        Photo,
        Name,
        氏名,
        部署,
        役職,
        携帯電話番号,
        誕生日
    )
);
Concurrent(
    ClearCollect(
        colDepartment,
        AddColumns(
            Distinct(
                colChild,
                Department
            ),
            VisibleStatus,
            true
        )
    ),
    ClearCollect(
        colYearMonth,
        AddColumns(
            Distinct(
                Sort(
                    colChild,
                    Birthday,
                    SortOrder.Descending
                ),
                Text(
                    Birthday,
                    "yyyy/mm"
                )
            ),
            VisibleStatus,
            true
        )
    )
)
```

[Concurrent 関数](https://learn.microsoft.com/ja-jp/power-platform/power-fx/reference/function-concurrent)を用いて、依存関係がないコレクションの設定を並列処理にしています。

https://learn.microsoft.com/ja-jp/power-platform/power-fx/reference/function-concurrent

## 2. コントロールの設定
次に`コントロール`を配置していきましょう。

**■ 構成**
 - **[高さが伸縮可能なギャラリー](https://learn.microsoft.com/ja-jp/power-apps/maker/canvas-apps/gallery-dynamic-sizing)**
     - 表示/非表示を切り替える`シェブロン`
     - 部署名または年月を表示する`テキストラベル`
     - **[空の垂直ギャラリー](https://learn.microsoft.com/ja-jp/power-apps/maker/canvas-apps/controls/control-gallery)**
         - コンテナー
             - 画像 コントロール - 社員のプロフィール写真を表示
             - テキストラベル - 社員名を表示
             - アイコン - 電話のアイコン
             - テキストラベル - 携帯電話番号
             - アイコン - 役職ありの方に表示するアイコン（役職がある場合、表示）
             - テキストラベル - 役職名

`ギャラリー コントロール`を`ネスト`させてコントロールを組み立てます。

### コントロールのプロパティ
**■ 親のギャラリー コントロール**
`Items`の設定値は、部署別の場合`colDepartment`、年月別の場合は`colYearMonth`です。

ギャラリーに含めている`シェブロン(アイコン)`の`OnSelect`に、下記の関数を仕込みます

```javascript:OnSelect
// 部署別の場合
UpdateIf(
    colDepartment,
    Value.Value = ThisItem.Value.Value,
    {
        Value: ThisItem.Value,
        VisibleStatus: !ThisItem.VisibleStatus
    }
)
// 年月別の場合
/*
UpdateIf(
    colYearMonth,
    Value = ThisItem.Value,
    {
        Value: ThisItem.Value,
        VisibleStatus: !ThisItem.VisibleStatus
    }
)
*/
```

部署別の場合、[UpdateIf 関数](https://learn.microsoft.com/ja-jp/power-platform/power-fx/reference/function-update-updateif)の表現が`Value.Value`という冗長的な状態になってしまっています。
元の列のデータ型が`選択肢`列のためです。

**■ 子のギャラリー コントロール**
`Items`の設定値を親のギャラリーコントロールの`ThisItem`を利用して、グループ化されたビューを実現します。

```javascript:Items(部署別)
Filter(colChild,Department.Value = ThisItem.Value.Value)
```

```javascript:Items(年月別)
Filter(colChild,Text(Birthday, "yyyy/mm") = ThisItem.Value)
```

子のギャラリーコントロールの`高さ(Height)`は、`レコードの一行の高さ × Self.AllItemsCount`とすることで、子のギャラリー コントロールの行数に応じた伸縮が実現されます。

```javascript:Height
// 一レコードの高さを40と設定する場合
40 * Self.AllItemsCount
```

ピシっと動的にサイズ変更がされて気持ちよいですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/af5282b2-1f21-aaee-d452-0247258fcc2a.png)

また`子のギャラリーコントロール`の`Visible`プロパティに`ThisItem.VisibleStatus`を設定することで、アイテムの表示/非表示のコントロールが実現されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/d7f18e48-2f9e-d1ef-cbb8-8e513d2cf559.png)

**なかなかPower Appsが映えて良い感じのビュー**になりますね。
そして紐解いていくと、あまり難しいロジックでもないことがわかります。

# 注意点
映える見た目、ということで機能を紹介しましたが、検証段階でもこの機能には問題があります。

大きなポイントとしては、[使用するコントロールが過剰になる傾向が挙げられます。](https://learn.microsoft.com/ja-jp/power-apps/maker/canvas-apps/performance-tips#%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%AB%E6%95%B0%E3%81%AE%E5%88%B6%E9%99%90)

今回紹介している機能だけでも`パフォーマンス`の課題が提起されるほどです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/dd4581d4-2db3-f1c3-d3ba-837ebfc5ccee.png)

対策として
- **コンポーネントとして作成する**
- **そもそもPower Appsでやらない（データのビューはSharePointでクイックに作成できる）**

このようなポイントが挙げられます。

https://learn.microsoft.com/ja-jp/power-apps/maker/canvas-apps/performance-tips#%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%AB%E6%95%B0%E3%81%AE%E5%88%B6%E9%99%90

いくらPower Appsが好きだから・・・といっても、なんでもPower Appsでやると痛い目を見ます(実体験)
車輪の再発明を避けて、Microsoft 365のサービスを組み合わせて、ベストプラクティスに基づいた仕組みづくりが理想ですね。