Power Apps クソアプリ リファクタリング計画(App.Formulas)

# はじめに
先日、自分が[Qiita](https://qiita.com/DEmodoriGatsuO/items/ecbd943c010a29bbd33b)で自信満々に発表したアプリがある。

https://qiita.com/DEmodoriGatsuO/items/ecbd943c010a29bbd33b

記事を出した当時は、自信満々だった。
しかし、ある記事に目を通し、その驕りを心から嘆いた。

それは、1年近くも前の[Microsoft社の公式 learn](https://learn.microsoft.com/ja-jp/power-apps/maker/canvas-apps/working-with-large-apps)

https://learn.microsoft.com/ja-jp/power-apps/maker/canvas-apps/working-with-large-apps

恥ずかしい気持ちでいっぱいだ。
なんって、クソアプリを発表してしまったんだ・・・。

これを読んでしまった猛者は、さぞ気分を害したに違いない。
善は急げ、だ。

**たった今から、このクソアプリのリファクタリングを始める。**

# App.OnStart → App.Formulas
致命的なのは`OnStart`だ。
便利だから、いつもこの枠に書き散らかしてしまう。


<details><summary>その結果うまれたクソコードがこちら</summary>

```
Set(i,1);
ClearCollect(colMenu,
    Table(
        {index: 1,ico:Icon.AddUser,for:"Recruiter",name:"01.採用者登録"},
        {index: 2,ico:Icon.OfficeBuilding,for:"Manager",name:"02.配属登録"},
        {index: 3,ico:Icon.Support,for:"Payroll",name:"03.個人情報"}
    )
);
ClearCollect(colEmployee,
    Table(
        {index: i,
         employeeNumber: "",
         fullName: "",
         birthDate: "",
         gender: "",
         address: "",
         phoneNumber: "",
         emailAddress: "",
         departmentName: "",
         position: "",
         salary: "",
         hireDate: "",
         terminationDate: "",
         healthInsuranceNumber: "",
         annualLeaveDaysLeft: ""}
    )
);
ClearCollect(colConfig,
    Table(
        {
            _menu: 1,
            _items: 6,
            _itemsConfig: {
                _itemY1: 80,
                _itemY2: 130,
                _itemY3: 180,
                _itemY4: 230,
                _itemY5: 280,
                _itemY6: 330
            },        
            _labelsConfig: {
                _label1: "氏名",
                _label2: "生年月日",
                _label3: "性別",
                _label4: "電話番号",
                _label5: "メールアドレス",
                _label6: "住所"
            },
            _labelConfig: {
                x: 130,
                w: 150,
                h: 40
            },
            _inpsConfig: {
                x: 280,
                w: 320,
                h: 40
            }
        },
        {
            _menu: 2,
            _items: 3,
            _itemsConfig: {
                _itemY1: 80,
                _itemY2: 130,
                _itemY3: 180,
                _itemY4: 0,
                _itemY5: 0,
                _itemY6: 0
            },        
            _labelsConfig: {
                _label1: "部署",
                _label2: "役職",
                _label3: "給与",
                _label4: "",
                _label5: "",
                _label6: ""
            },
            _labelConfig: {
                x: 130,
                w: 150,
                h: 40
            },
            _inpsConfig: {
                x: 280,
                w: 360,
                h: 40
            }
        },
        {
            _menu: 3,
            _items: 4,
            _itemsConfig: {
                _itemY1: 80,
                _itemY2: 130,
                _itemY3: 180,
                _itemY4: 230,
                _itemY5: 0,
                _itemY6: 0
            },        
            _labelsConfig: {
                _label1: "社員番号",
                _label2: "入社日",
                _label3: "健康保険番号",
                _label4: "年次有給休暇",
                _label5: "",
                _label6: ""
            },
            _labelConfig: {
                x: 130,
                w: 150,
                h: 40
            },
            _inpsConfig: {
                x: 280,
                w: 360,
                h: 40
            }
        }
    )
);
```
</details>

汚すぎる。そして長い。
変数名も含めて意味が分からない。

```json-doc:colConfigに至ってはダブりだらけ
// 何回かいているんだコレ
{
    _labelConfig: {
        x: 130,
        w: 150,
        h: 40
    },
    _inpsConfig: {
        x: 280,
        w: 360,
        h: 40
    }
}
```

まあいい。書いたくそコードの数だけ強くなれるって誰かが言っていた気がする。
無知の無知である状態は仕方ない。

ただ改善策を知った。
放置は罪。

これを`App.Formulas`に変えてみる。

[App.Formulas](https://learn.microsoft.com/ja-jp/power-platform/power-fx/reference/object-app#formulas-property)の説明はコチラ。

https://learn.microsoft.com/ja-jp/power-platform/power-fx/reference/object-app#formulas-property

## [App.Formulas](https://learn.microsoft.com/ja-jp/power-platform/power-fx/reference/object-app#formulas-property)の利点

書いての通り、**名前付き関数** としてあらゆるところから呼び出せる。
`カスタム コンポーネント` で実現できると思っていたが、それ以前に`App.Fomulas`で解決する課題は多そうだ。

実際に書いて、使ってみると、

1. 可読性が高い
1. メンテナンスし易い
1. 速い

> **名前付き計算式は Excel の概念です。** 
>> Microsoft learnより

なんって素敵な響きだ。
Excelは私の故郷だ。故郷を考えない作り方をしてしまったというのか・・・。
おお、私よ、情けない。

原点に立ち返るのだ。
トコトンめんどくさがって、同じことは何回も書かないのだ・・・。

同じ処理は可能な限り、考えて、まとめよう。
アプリ作成が大変で効率化どころか仕事になったなんていったら本末転倒だ。

## App.Formulasに変えてみた全体像

汚いところはごめんなさい🙇

```
// 1. よく使うカラーを定義
// - App.Theme.Colorsをメインで使う前提
ColorAppsTheme = RGBA(91, 95, 199, 1);

// 2. メニューのギャラリーを関数で設定
InitializeMenuData = 
    Table(
    { Index: 1, Icon: Icon.AddUser, Role: "Recruiter", MenuName: "01.採用者登録" },
    { Index: 2, Icon: Icon.OfficeBuilding, Role: "Manager", MenuName: "02.配属登録" },
    { Index: 3, Icon: Icon.Support, Role: "Payroll", MenuName: "03.個人情報" }
);

// 3. 空のレコードの挿入
InitializeEmployeeData = 
        Table(
            {
                EmployeeNumber: "",
                FullName: "",
                BirthDate: "",
                Gender: "",
                Address: "",
                PhoneNumber: "",
                EmailAddress: "",
                DepartmentName: "",
                Position: "",
                Salary: "",
                HireDate: "",
                TerminationDate: "",
                HealthInsuranceNumber: "",
                AnnualLeaveDaysLeft: ""
            }
        );


// 4. コントロール プロパティの定義
FormStyle = 
    {
        Control: { Height: 40 },
        Inputs: { Width: 320, X: 280 },
        Label: { Width: 150, X: 130 ,
            Y: {
                Y1: 80,
                Y2: 130,
                Y3: 180,
                Y4: 230,
                Y5: 280,
                Y6: 330
            }
        }
    };


// 5. ラベル コントロールのテキストを定義
LableTexts =
    Table(
        {
            Index: 1,
            LabelTexts:
                {
                    Label1: "氏名",
                    Label2: "生年月日",
                    Label3: "性別",
                    Label4: "電話番号",
                    Label5: "メールアドレス",
                    Label6: "住所"
                }
        },
        {
            Index: 2,
            LabelTexts:
                {
                    Label1: "部署",
                    Label2: "役職",
                    Label3: "給与"
                }
        },
        {
            Index: 3,
            LabelTexts:
                {
                    Label1: "社員番号",
                    Label2: "入社日",
                    Label3: "健康保険番号",
                    Label4: "年次有給休暇"
                }
        }
    );
```

一見大差ないように見える部分もあるが、**決してそうではない。**
関数は毎回呼び出される。

`OnStart`というタイミングで一斉に評価をせずに済むということがポイント。

> **App.OnStart** は、長い数式を構成する大きな原因の一つであり、間違いなくそこから始めるべきですが、唯一のケースというわけではありません。<br>
当社の調査によると、Power Apps Studio の読み込み時間の長いアプリのほぼ全てに、256,000 文字を超える数式が少なくとも 1 つあることが分かっています。 読み込み時間が最も長い一部のアプリには、100 万文字を超える数式が含まれています。 長い数式は、大きな負担を Power Apps Studio にかけます。
>> [Microoft learn](https://learn.microsoft.com/ja-jp/power-apps/maker/canvas-apps/working-with-large-apps#split-up-long-formulas)より

耳が痛い。
`OnStart`に書きまくる張本人だ。

恥ずかしくなって、私は`OnStart`の中身を全部消した。

なぜ`OnStart` に依存するかというと、最初に`静的な値` **定数** 的な使い方をしたくなる。
データソースをつなぐまでもないな、と思うテーブルは、`Table関数` で書きたくなる。

結果的に、非常に`OnStart`の数式が長くなる。
わかりきっていることだったが、見解が甘かった。

Power Appsの数式の評価は高速だ。
すぐに読み込まれることを織り込んで、関数を活用する。

改善ポイントをいくつか紹介したい。

## 実際の速さの差異

![マイビデオ.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/66661b19-6722-b86f-fd63-c13dcecfbd24.gif)

:::note warn
色のメンテナンスの関係か、そもそものアプリでも負荷が少なかったか、
わかりづらさは否めなくない
元のアプリがミスっているのか、コントロールが表示されていない・・・
:::

## 1. 定数のように扱う

```plaintext:よく使うカラーを定義
ColorAppsTheme = RGBA(91, 95, 199, 1);
```

繰り返し使うもの、`色`は典型。

上記を`App.Formulas`に設定することで、`Color`や`Fill`属性に、
`ColorAppsTheme`で設定を統一することができる。

`App.Formulas`は関数なので、そのコントロールが必要になるときに、式が評価される。
細かく処理がPower Appsの中で分割するので、`OnStart`のタイミングで、Power Appsの処理負荷を集中させずに済む。

そのほかの色については、`App.Theme`を多用している。
今回は`Teams`をテーマに設定した。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/91dee4a4-2450-0863-f5be-d96fb37289d4.png)


`App.Theme` プロパティ基軸に、色合いを決定する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/b8c9acf1-0a0c-8777-3807-c428623d94b1.png)


```plaintext:コントロール値を制御する
// コントロール プロパティの定義
FormStyle = 
    {
        Control: { Height: 40 },
        Inputs: { Width: 320, X: 280 },
        Label: { Width: 150, X: 130 ,
            Y: {
                Y1: 80,
                Y2: 130,
                Y3: 180,
                Y4: 230,
                Y5: 280,
                Y6: 330
            }
        }
    };
```

`Y1`～の部分はダサいが、`コントロール`の`高さ`や`幅`を、`レコード`に集約している。
`FormStyle.Control.Height`で`40`が呼び出される。

コレクション`colConfig`でダサい書き方をしていたが、
こちらのほうが**圧倒的にスマート**だ。

:::note info
アプリの修正にかかる時間を短縮するうえでは[検索と置き換え]()を多用するに越したことはない。
くそコードからスマートな書き方へ、気軽に変えていこう。
:::

```plaintext:固定的なテーブルを設定
// 2. メニューのギャラリーを関数で設定
InitializeMenuData = 
    Table(
    { Index: 1, Icon: Icon.AddUser, Role: "Recruiter", MenuName: "01.採用者登録" },
    { Index: 2, Icon: Icon.OfficeBuilding, Role: "Manager", MenuName: "02.配属登録" },
    { Index: 3, Icon: Icon.Support, Role: "Payroll", MenuName: "03.個人情報" }
);
```

変動しない`テーブル型`のデータもこのようにまとめて置ける。
~~ぶっちゃけギャラリー コントロールのItemsに書いておいて~~
やらなくて良くないかとも感じたが、`App.Formula` で、**列挙されているのは、保守性の観点でいい**。

## 2. 既存の式に使う
`Collection`を一から作るとき、カラム名がベタ打ちになり、式が冗長的になる。

空のレコードを`Collect`で追加するにも、長ったらしい式になる。

```plaintext:修正前の空レコードの挿入
Set(i,i + 1);

Collect(colEmployee,
        Table(
            {
                Index: i,
                EmployeeNumber: "",
                FullName: "",
                BirthDate: "",
                Gender: "",
                Address: "",
                PhoneNumber: "",
                EmailAddress: "",
                DepartmentName: "",
                Position: "",
                Salary: "",
                HireDate: "",
                TerminationDate: "",
                HealthInsuranceNumber: "",
                AnnualLeaveDaysLeft: ""
            }
        );
);
```

それを`App.Formulas`であらかじめ下記を設定しておくと、

```plaintext:App.Formulas
// 3. 空のレコードの挿入
InitializeEmployeeData = 
        Table(
            {
                EmployeeNumber: "",
                FullName: "",
                BirthDate: "",
                Gender: "",
                Address: "",
                PhoneNumber: "",
                EmailAddress: "",
                DepartmentName: "",
                Position: "",
                Salary: "",
                HireDate: "",
                TerminationDate: "",
                HealthInsuranceNumber: "",
                AnnualLeaveDaysLeft: ""
            }
        );
```

非常に美しくなる👀✨
```plaintext:修正後の空レコードの挿入 OnSelect
Set(i,i + 1);

Collect(colEmployee,
    AddColumns(InitializeEmployeeData,
        "Index", i
    )
);
```

これは感動的だ・・。
特に冗長的になる`Table関数`が、整理されるのは嬉しい。

### LookUp 参照先として利用する
前述のテーブル型として書いておけば・・・
```plaintext:App.Formulas
// 5. ラベル コントロールのテキストを定義
LableTexts =
    Table(
        {
            Index: 1,
            LabelTexts:
                {
                    Label1: "氏名",
                    Label2: "生年月日",
                    Label3: "性別",
                    Label4: "電話番号",
                    Label5: "メールアドレス",
                    Label6: "住所"
                }
        },
        {
            Index: 2,
            LabelTexts:
                {
                    Label1: "部署",
                    Label2: "役職",
                    Label3: "給与"
                }
        },
        {
            Index: 3,
            LabelTexts:
                {
                    Label1: "社員番号",
                    Label2: "入社日",
                    Label3: "健康保険番号",
                    Label4: "年次有給休暇"
                }
        }
    );
```

非常に短い式で、レコード抜き出すだけの関数で変数への格納が完了する。
```plaintext:OnSelectでLookUpに応用
Set(LabelText,LookUp(LableTexts,Index=ThisItem.Index).LabelTexts);
```

[今回のアプリはGitHubへ](https://github.com/DEmodoriGatsuO/PowerPlatform-Events-Sample/tree/main/99Qiita/20240124)

https://github.com/DEmodoriGatsuO/PowerPlatform-Events-Sample/tree/main/99Qiita/20240124

# おわりに
クソコードをさらしたことは恥ずかしいが、これを機に書き方を見直そうと思う。
`無知の無知`は確かに恥ずかしいが、`無知の知`に至ったので、さらに先に進めるだろう・・・。

反省点として・・・
**まずは[公式](https://learn.microsoft.com/ja-jp/power-apps/maker/canvas-apps/efficient-calculations)を読もう！**

https://learn.microsoft.com/ja-jp/power-apps/maker/canvas-apps/efficient-calculations

**良いPower Lifeを！**