Power Apps でデータから自動生成した電話形式アプリをタブレット形式に移植してみる

#はじめに
本記事は、

「データから自動生成した電話形式アプリ（以下、自動生成アプリ）のようなアプリを、タブレット形式で作成したいのだけど、どこから手を付けてよいか分からない！」

という方向けに、自動生成アプリの構成（の一部）を学びながら、自動生成アプリをタブレット形式のアプリに移植するための手順書です。
Power Apps 初学者の方、あるいは周りの初学者の方にタブレット形式のアプリの作り方を教える方に、何かご参考になれば幸いです。


#この手順書を作成した背景
弊社では「自動生成アプリを1日も早く簡易メンテナンスできるようにする」ことを目的として、以下の構成でこれまで1回1時間のボリュームで Power Apps 勉強会を開催してきました。

【概要説明編】
　第1回　Power Apps とは？
　第2回　各コントロールの機能やプロパティ / 他オブジェクトのプロパティ参照
　第3回　ギャラリー / データ接続
　第4回　フォーム

【実践編 ～自分で残業申請アプリを作ってみよう～】
　第5回　データとは？ / SharePoint リスト作成 / "データから開始"でアプリ自動生成
　第6回　自動生成アプリのメンテナンス方法 / 条件分岐 / 日付フィルタ
---以後、予定---
　第7回　委任回避術（ユーザーフィルタ、日付フィルタ）
　第8回　残業申請アプリを完成させる（もくもく会）

【Power Automate編】
　第9回～（内容未定）


※ 変数には触れていない
※ 数式/関数は都度使うもののみ紹介
※ データの説明が第5回って遅すぎない？？（自戒）
<br>
目的が目的のため、すべての回が電話形式のアプリを対象としています。
そのため、タブレット形式でアプリを1から作成する方法についてはまだ触れていません。

ただ、社内には既にタブレット形式のアプリを作りたいという需要も、少しずつ出てきたんですね。
<br>
さて、これまでの勉強会の内容も踏まえて、タブレット形式のアプリの作り方をどのように教えるのが効率的なのか。


#本記事のゴール
そこで私は、自動生成アプリのように、タブレット形式でもまずはシンプルなものを簡単に作ることができれば、そこからのカスタマイズが容易になるのではないかと考えました。
そこで本記事では、以下のアプリを作ることをゴールとします。
![test.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/575feffd-44b5-4b9b-f0a8-b0dd81fe49d3.gif)

見ての通り、自動生成アプリの一覧画面（BrowseScreen1）を左側に、フォーム編集画面（EditScreen1）を右側に配置しただけのような見た目です。
個人的には、読み取り専用のフォーム（DetailScreen1）はなくてもさほど問題にはならないと思っていて、ギャラリーで選択したら直接フォーム編集できればいいのでは、という意見です。
（色々な考え方があるかと思いますので、あくまで一意見として）

これを1から自力で作成するには、自動生成アプリの構成をおおよそ理解する必要がありますが、初学者にはややハードルが高いのではないかと思います。
デザイン性にも優れたテンプレートアプリからカスタマイズする方法もあるのですが、テンプレートのアプリはコントロールの構成やプロパティの制御が複雑なものが多く、単純に1つのデータソースからシンプルなアプリをタブレット形式で作成したい、という需要にはあまりマッチしていないように思います。

そこで、上記のシンプルなタブレットアプリを、自動生成アプリから簡単に移植する方法があれば、Power Apps をまだ十分に理解していない方でも簡単に成功体験ができ、そこからのカスタマイズを通して理解を深めるスピードが早まるのではないかと考えました。


#自動生成アプリの構成
本記事で紹介する移植手順を紹介する上で、最低限理解が必要な構成について紹介します。

##全体像
アプリをデータから自動生成すると、以下の 3 つの画面（スクリーン）が自動生成されます。

- BrowseScreen1（データの一覧を表示する画面）
- DetailScreen1（一覧で選択したレコードの詳細を表示する画面）
- EditScreen1（一覧で選択したレコードの編集や新規入力を行う画面）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/4dcf03f8-1486-1928-2cf1-270ef489a0cb.png)
本記事ではまず、上記のうち BrowseScreen1 と EditScreen1 の中身を移植するところからスタートします。


##画面遷移図
自動生成アプリは、以下のように画面遷移するよう、各コントロールに関数が記載されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/f03b3311-4cfe-e255-358d-af2247943dc2.png)
本記事ではタブレットの1画面にすべて移植するため、画面遷移は不要となります。
よって、上図で処理されている画面遷移関数を削除する手順が発生します。

【参考】
画面遷移を行う関数については、以下の記事を参照ください。
[Power Apps の Back および Navigate 関数](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/functions/function-navigate)

##フォームのモード
さて、画面遷移図を見ると、自動生成アプリではいわゆる「新規フォーム」と「編集フォーム」は同じ画面（同じ編集フォーム コントロール）で制御していることが分かります。
編集フォーム コントロールは"モード"と呼ばれる状態を持っていて、フォームの"モード"を切り替えることによって、新規と編集を分けて制御しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/1c1e0006-31b5-98ab-3738-18d22ad3577f.png)

##代表的なアクション
フォームのモードを含め、自動生成アプリを制御する上で重要なアクションを紹介します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/22d5fe78-443a-74cc-23ab-36a0f3f6f976.png)
本記事では DetailScreen1 は移植しません。
よって、先ほど述べた画面遷移関数の削除作業に加えて、ごみ箱アイコンの機能（該当レコードの削除）とその隣にある編集アイコンの機能（フォームを編集モードに変更）をどこかに移植する手順が必要となります。

【参考】
編集フォームのモードや、データの登録 / 更新処理については、以下の記事を参照ください。
[Power Apps での EditForm、NewForm、SubmitForm、ResetForm、ViewForm 関数](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/functions/function-form)
レコードの削除処理については、以下の記事を参照ください。
[Power Apps の remove および RemoveIf 関数](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/functions/function-remove-removeif)


#移植手順
前述した自動生成アプリの構成を踏まえ、以下の手順で移植を行います。

1. 事前準備
2. データソースに接続する
3. コピる
4. 編集アイコンの機能を移植する
5. ごみ箱アイコンの機能を移植する
6. 画面遷移処理を削除する
7. その他

なお、移植手順には座標の話が含まれますので、Power Apps の座標系については先人が書かれた以下の記事をご覧ください。
[PowerAppsでレスポンシブなアプリを作る @MasatoFukakusa ](https://qiita.com/MasatoFukakusa/items/73bdf54c78ff036933cd)
[#PowerApps における数学 #1 @h-nagao ](https://qiita.com/h-nagao/items/e5e7a3708ddb5a10ce0e)

##事前準備
自動生成アプリの編集画面と、タブレット（中身は空）アプリの編集画面を用意します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/dc63fcce-9c30-8c3f-8099-a5d2a3225d4c.png)


##データソースに接続する
自動生成アプリで選択したデータソースに、タブレットアプリから接続します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/8f6f77de-1d9e-5a12-cf55-f1443d0f7286.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/faf66d9d-bead-e2a4-1517-3cc05cd5f77a.png)


##コピる
自動生成アプリの各コントロールを、何段階かに分けてコピペし、都度調整していきます。
各コントロールは各々の位置に依存関係があり、一気にコピペすると崩れてしまうため、本手順では少しずつコピペしていきます。

###BrowseScreen1 からの移植
ツリービューの下（最背面）から順にコピペしていきます。

1. RectQuickActionBar1 をコピペし、Y を 0 にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/a38992d1-94c4-efb3-071b-e41f4a995cb5.png)

2. 本コントロールは幅（Width）が画面サイズ（Parent.Width）になるよう設定されているため、幅を画面サイズの半分（Parent.Width/2）にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/30186283-a3ca-ffd1-990f-bf5e23281def.png)

3. 続いて、下図の 4 つのコントロールをコピペし、コピペしたコントロールの Y を 0 にしておきます。
"＋"アイコンでエラーマークが表示されますが、後の手順で修正しますので、ここではこのままにしておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/56ea2183-dc3d-6e5b-9b28-d233014b8beb.png)

4. ラベル（LblAppName1）の幅（Width）、および 3 つのアイコンの X は画面サイズ（Parent.Width）に依存しているため、各々のプロパティに記載されている画面サイズをそれぞれ半分（Parent.Width/2）にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/bc4f890f-215f-6b45-19c3-6c457c368772.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/c13fe89d-c11f-012f-a12b-11499fb46b0d.png)

5. 続いて、下図の 3 つのコントロールをコピペします。
これらの Y は調整しなくてもぴったり収まります。気になる方は各々の Y の数式を参照してみてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/28a328ec-2a3d-d4b3-3269-9b7b63e82d98.png)

6. テキスト入力（TextSearchBox1）と下線（Rectangle11）は幅（Width）が画面サイズ（Parent.Width）になるよう設定されているため、幅を画面サイズの半分（Parent.Width/2）にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/2d79e955-124d-708c-0b45-ee28d0a498fe.png)

7. 最後に、ギャラリー（BrowseGallery1）をコピペします。
この Y も、調整しなくてもぴったり収まりますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/339c78b0-2ede-d067-2f22-24b4f34552f8.png)

8. 同様に、幅（Width）が画面サイズ（Parent.Width）になるよう設定されているため、幅を画面サイズの半分（Parent.Width/2）にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/61d43f51-5e6c-b881-60b1-398b1b2f9e2f.png)

これで、BrowseScreen1 からの移植は完了です。

###EditScreen1  からの移植
BrowseScreen1 と同様に、ツリービューの下（最背面）から順にコピペしていきます。

1. RectQuickActionBar3 をコピペし、Y を 0 にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/ef7004f3-ba78-dce8-39d3-7d3e880141a3.png)

2. 本コントロールは幅（Width）が画面サイズ（Parent.Width）になるよう設定されているため、幅（Width）を画面サイズの半分（Parent.Width/2）にし、X の位置も Parent.Width/2 にします。
Parent.Width/2 は、画面サイズの半分の値であり、かつ画面の中央の座標を意味します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/781c1c8f-5ece-6151-f63b-8e8e5952908e.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/f71728dc-17ea-11a1-b5a3-fbb606fb77e0.png)

3. 続いて、下図の 3 つのコントロールをコピペし、コピペしたコントロールの Y を 0 にしておきます。
"×"アイコンと"✓"アイコンにエラーマークが表示されますが、後の手順で編集フォームを挿入することで解消しますので、ここではこのままにしておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/cc895d17-c659-1474-2dfb-84e8b54502a7.png)

4. "×"アイコン（IconCancel1）の X を画面の中央の座標（Parent.Width/2）にします。
テキスト入力コントロール（LblAppName3）は"×"アイコンの位置に依存しているため、見やすい位置に自動的に移動されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/4ac56a40-c796-a8e3-7a16-73d10988b704.png)

5. テキスト入力コントロール（LblAppName3）は幅（Width）が画面サイズ（Parent.Width）に依存しているため、Width に記載されている画面サイズを半分（Parent.Width/2）にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/4b52b3d7-a5ed-e084-f1d7-d560f4f6d4e3.png)

6. 最後に、編集フォーム コントロール（EditForm1）をコピペします。
この Y も、調整しなくてもぴったり収まりますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/d9bffd41-78ad-8153-d8f7-3bd05a915093.png)

7. 本コントロールは幅（Width）が画面サイズ（Parent.Width）になるよう設定されているため、幅（Width）を画面サイズの半分（Parent.Width/2）にし、X の位置も Parent.Width/2 にします。
Parent.Width/2 は、画面サイズの半分の値であり、かつ画面の中央の座標を意味します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/75d061b9-389d-c190-7f97-9b0541d12929.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/d279c3aa-c710-a4f9-b7c3-ec804adc1c32.png)

これで、EditScreen1 からの移植は完了です。


##編集アイコンの機能を移植する
前述の手順では、DetailScreen1 内にある編集アイコン（IconEdit1）が移植されていません。
「自動生成アプリの構成」の章で述べた通り、編集フォームは"新規モード"と"編集モード"を持っているため、どこかで"編集モード"に切り替える必要があります。
そこで本記事では、「本記事のゴール」の動画で表現した通り、Gallery でとあるレコードを選択した時点で"編集モード"に切り替えることにします。

1. DetailScreen1 内の編集アイコン（IconEdit1）の OnSelect プロパティに記載されている"編集モード"への切り替え処理「EditForm(EditForm1);」をコピペし、コピー先のギャラリー（BrowseGallery1）の OnSelect プロパティに記載します（元々記載されていた処理は削除してください）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/f70672d3-34c8-174e-9c98-3858facaa447.png)
※OnSelect プロパティは、コントロールをクリックした際に動作させたい処理を記述するものです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/e8be68d1-3d28-bd06-3868-7274fbe50273.png)


これで、編集アイコンの機能の移植は完了です。
なお、編集フォームを"新規モード"に切り替える処理は、その機能を有する"＋"アイコンを移植済みのため、別途作業は不要です。


##ごみ箱アイコンの機能を移植する
前述の手順で移植していない DetailScreen1 内のごみ箱アイコンを移植します。
ちょっとひと工夫も。

1. DetailScreen1 内のごみ箱アイコン（IconDelete1）をコピペし、お好きな位置にドラッグします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/d843a017-ccef-768b-96f5-5006722b4cbc.png)

2. ここでごみ箱アイコンを選択した時の削除処理を見てみます。
この処理は「データソース（ここでは"気分チェッカー"）のうち、"BrowseGallery1"で選択されているものを削除する」という意味になっています。
で、今何が選択されているかは、編集フォームが"編集モード"であればデータが表示されているので分かるのですが、"新規モード"の場合は裏で何が選択されているのかが分かりません。
そこで、編集フォームが"新規モード"の場合はごみ箱アイコンを表示しないようにすることにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/d692bcb1-5d71-8cec-ad06-741bad3749da.png)

3. ごみ箱アイコンの Visible プロパティに以下を記載します。
Visible プロパティは、そのコントロールを表示するかどうかを True/ False で設定するものです。
編集フォームは「編集フォーム名.Mode」で現在のモードが取得できるため、現在のモードが新規（New）の場合は非表示（False）にし、それ以外の場合は表示（True）する設定です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/845c52ce-a3aa-8b7a-51c1-5ae45eb46281.png)

    ```php:Visible
    If(EditForm1.Mode=New, false, true)
    ```

これで、ごみ箱アイコンの機能移植は完了です。


##画面遷移処理を削除する
一部のコントロールに残っている画面遷移処理を削除します。

1. "＋"アイコン（IconNewItem1）の OnSelect プロパティから、画面遷移処理 Navigate 関数を削除します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/650c884e-7953-6234-87d4-67401dbc94bb.png)

これ以外にも 2 箇所で"前の画面に戻る"処理を行う Back 関数が記載されていますが、エラーにはなっていないのでよしとします（強引）。

これで完成！でもよいのですが、最後に個人的に違和感を感じる挙動を調整してみます。


##その他：フォームで何かアクションしたら新規モードにする
この状態だと、"✓"アイコンをクリックしてデータを登録した後や、ごみ箱アイコンをクリックして指定レコードを削除した後に、編集フォーム上に何かしら選択されたレコードのデータが表示されます。
自動生成された電話形式のアプリでは、それらの操作をした直後に編集フォームの画面を見ることはないため、特に何も感じないのですが、今回作成したタブレットアプリでは、「え？これいつ選択したレコードのデータだっけ？」と感じる上、そのまま勢い余ってごみ箱アイコンをクリックしてしまいそうになることが多々ありました。
そこで、"✓"アイコンやごみ箱アイコンをクリックした際には、編集フォームのモードを"新規モード"に変更するようにしました。
なお、"新規モード"への変更は、"＋"アイコンの OnSelect の処理である「NewForm(EditForm1);」で行えます。

1. ごみ箱アイコン（IconDelete1）の OnSelect プロパティ内の Back() を、NewForm(EditForm1); に書き換えます。
この Back() は、削除処理が正常に終了した際に前の画面に戻るもので、ここを"新規モード"への変更処理に書き換えることで、削除処理が正常に終了した際に編集フォームを"新規モード"に変えることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/3305defa-d9ef-c341-c2ff-6e1ff649635c.png)

2. 編集フォーム（EditForm1）の OnSuccess プロパティ内の Back() を、NewForm(EditForm1); に書き換えます。
この Back() は、"✓"アイコンクリック時に行われるデータ登録処理が正常に終了した際に前の画面に戻るもので、ここを"新規モード"への変更処理に書き換えることで、データ登録処理が正常に終了した際に編集フォームを"新規モード"に変えることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/7e52e7f9-d10b-1981-881a-ba771fd1fb81.png)

以上で、すべての作業は完了です。


#まとめ
他ちょちょいと整えると、本記事の始めの方で紹介したゴールのようなアプリが出来上がるかと思います。
![test.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405544/575feffd-44b5-4b9b-f0a8-b0dd81fe49d3.gif)
※あ、画面遷移がないから”×”アイコンもいらないですね…（今更）
<br>
本手順で作成（というか移植）したアプリは、完成度で言えばまだ作成のスタート地点です。
“×”アイコンや Back() など余計な処理が含まれている上、デザインや機能の向上、場合によってはエラー処理などを別途考える必要があるかもしれません。

ただ、本記事によって、1人でも多くの方がタブレットアプリを作成するきっかけを得られたり、タブレットアプリの作成の幅を広げられればいいなと思っています。
<br>
これからは、欲しいアプリは欲しい人が作り、欲しいデータは欲しい人が集める時代です。
ハッピーになりたい人が自分の力でハッピーになれる世界へ。

何かのご参考になれば幸いです。