11
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2024-01-24

はじめに

先日、自分がQiitaで自信満々に発表したアプリがある。

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

それは、1年近くも前のMicrosoft社の公式 learn

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

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

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

App.OnStart → App.Formulas

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

その結果うまれたクソコードがこちら
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
            }
        }
    )
);

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

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

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

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

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

App.Formulasの説明はコチラ。

App.Formulasの利点

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

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

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

名前付き計算式は 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 は、長い数式を構成する大きな原因の一つであり、間違いなくそこから始めるべきですが、唯一のケースというわけではありません。

当社の調査によると、Power Apps Studio の読み込み時間の長いアプリのほぼ全てに、256,000 文字を超える数式が少なくとも 1 つあることが分かっています。 読み込み時間が最も長い一部のアプリには、100 万文字を超える数式が含まれています。 長い数式は、大きな負担を Power Apps Studio にかけます。

Microoft learnより

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

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

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

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

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

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

実際の速さの差異

マイビデオ.gif

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

1. 定数のように扱う

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

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

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

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

そのほかの色については、App.Themeを多用している。
今回はTeamsをテーマに設定した。
image.png

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

image.png

コントロール値を制御する
// コントロール プロパティの定義
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.Height40が呼び出される。

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

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

固定的なテーブルを設定
// 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で追加するにも、長ったらしい式になる。

修正前の空レコードの挿入
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であらかじめ下記を設定しておくと、

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

非常に美しくなる👀✨

修正後の空レコードの挿入 OnSelect
Set(i,i + 1);

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

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

LookUp 参照先として利用する

前述のテーブル型として書いておけば・・・

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: "年次有給休暇"
                }
        }
    );

非常に短い式で、レコード抜き出すだけの関数で変数への格納が完了する。

OnSelectでLookUpに応用
Set(LabelText,LookUp(LableTexts,Index=ThisItem.Index).LabelTexts);

今回のアプリはGitHubへ

おわりに

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

反省点として・・・
まずは公式を読もう!

良いPower Lifeを!

11
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?