この記事は学習メモとして書いています。間違いや改善点があれば随時更新します。
はじめに
業務で、SharePointリストをデータソースとした管理台帳アプリをPower Appsで開発しました。アプリの起動時に、ユーザーの権限設定情報をSharePointリストから取得し、その結果に応じてボタンの表示/非表示を切り替えたり、画面上部にユーザー名を表示したりする機能を実装する必要がありました。
この起動時の初期化処理を実装する過程で、以下のような問題に直面しました。
- 画面が表示された瞬間、権限のないユーザーにもボタンが一瞬見えてしまう
- ユーザー名やロール情報が遅れて表示され、画面がチラつく
この問題を解決する過程で、App.FormulasやApp.OnStartの仕様を調べ、最終的にLoadingScreen(読み込み中画面)のOnVisibleで初期化処理を行い、非表示ボタンのSelect関数で画面遷移するという方法にたどり着きました。
本記事では、その過程で学んだことを書き留めておきます。
App.Formulasとは
App.Formulasは、Power Appsの「名前付き数式」を定義するプロパティです。アプリ全体で使える定数や計算式を宣言的に定義できます。
// App.Formulas の記述例
gblIsAdmin = !IsBlank(LookUp(ユーザー権限管理一覧, メールアドレス = User().Email));
FormattedUserName = Last(Split(User().FullName, " ")).Value & " " &
First(Split(User().FullName, " ")).Value;
gblImportButton = gblIsAdmin;
App.Formulasの特徴は以下のとおりです。
-
Set関数を使わず、宣言的に値を定義できる - 定義した名前はアプリ全体のどの画面からでも参照できる
- 参照先のデータが変われば自動的に再計算される
一見すると、アプリの初期化処理はすべてApp.Formulasに書けば良さそうに思えます。しかし、実際に使ってみると落とし穴がありました。
App.Formulasの落とし穴 — 遅延評価
App.Formulasに定義した数式は、**値が実際に参照されるタイミングで計算される(遅延評価)**という仕様があります。
当初、権限チェックをApp.Formulasに定義し、StartScreenに設定していた検索画面がアプリ起動時に表示されるようにしていました。
// App.Formulas に定義
gblIsAdmin = !IsBlank(LookUp(ユーザー権限管理一覧, メールアドレス = User().Email));
gblImportButton = gblIsAdmin;
ところが、アプリを公開してブラウザで起動してみると、検索画面表示直後にこのような現象が起きました。
- ボタンが一瞬表示され、読み込みが完全に終わったあとにボタンが非表示になる
原因は、App.FormulasのgblIsAdminがSharePointリストへのLookUpを含んでいるため、画面が遷移した時点ではまだ計算が完了していなかったことでした。遅延評価の仕様上、値が必要になったタイミングで初めてSharePointへの問い合わせが走るため、画面表示に間に合わないケースがあります。
この経験から、外部データソースへのアクセスを伴う処理をApp.Formulasに書くと、表示タイミングを制御できない可能性があるということを学びました。
App.OnStartについて
App.OnStartは、アプリ起動時に一度だけ実行されるプロパティです。
// App.OnStart の記述例
Set(gblIsAdmin, false);
Set(
gblIsAdmin,
!IsBlank(LookUp(ユーザー権限管理一覧, メールアドレス = User().Email))
);
App.OnStartの特徴は以下のとおりです。
- 処理が上から順に同期的に実行される
- App.OnStartが完了するまで画面は一切表示されない
-
Set関数で変数に値を格納できるため、処理の順序と完了タイミングを明確に制御できる
App.Formulasの遅延評価の問題を踏まえると、App.OnStartの方が初期化処理には向いているように見えます。実際、一時的にApp.OnStartで権限チェックやユーザー名の設定を行う構成にしたところ、チラつきの問題は解消しました。
しかし、App.OnStartにはMicrosoftから非推奨の流れが示されているという点があります。将来的に仕様変更や廃止の可能性を考慮すると、App.OnStartに多くの処理を依存させるのはリスクがあると判断しました。
App.OnStartは現時点では使用可能ですが、MicrosoftはApp.Formulasや画面のOnVisibleへの移行を推奨しています。将来的な不具合や仕様変更のリスクを考慮し、本記事ではApp.OnStartへの依存を最小限にする方針で設計しています。
処理の使い分け — どこに何を書くか
ここまでの経験を踏まえ、以下のように処理を整理しました。
| 設定箇所 | 適している処理 | 理由 |
|---|---|---|
| App.Formulas | 外部データアクセスを伴わない定数的な定義 | 遅延評価のため、外部アクセスでは表示タイミングを制御できない |
| App.OnStart | 使用しない(空にする) | 非推奨の流れがあり、将来的な不具合リスクを回避するため |
| LoadingScreen.OnVisible | 権限チェック、ユーザー名設定、ログ登録など |
Setで同期的に実行でき、処理完了後に画面遷移を制御できる |
LoadingScreen.OnVisibleに初期化処理を集約することで、以下のメリットが得られます。
- App.OnStartに依存しないため、非推奨に伴うリスクを回避できる
-
Setによる同期実行で、すべての値が確定してから画面遷移できる - LoadingScreenが表示されている間に処理が走るため、ユーザーには「読み込み中」が見える
解決策:LoadingScreen+非表示ボタンSelectの実装
全体の処理フロー
アプリ起動
↓
App.OnStart(空)
↓
App.StartScreen → LoadingScreen が表示される
↓
LoadingScreen.OnVisible 実行
├ 変数の初期化
├ 権限チェック(SharePointリストを参照)
├ ユーザー名の整形
├ 起動ログの登録
└ Select(btnNavigate) ← 非表示ボタンを自動押下
↓
btnNavigate.OnSelect → Navigate(SearchScreen)
↓
検索画面に遷移(すべての変数が確定済み)
App.OnStart
// 空(処理なし)
App.StartScreen
LoadingScreen
LoadingScreenの構成
LoadingScreenには以下のコントロールを配置します。
ラベル(lblLoading)
| プロパティ | 値 |
|---|---|
| Text | "読み込み中..." |
| Align | Center |
| X | 0 |
| Y | Parent.Height / 2 - Self.Height / 2 |
| Width | Parent.Width |
非表示ボタン(btnNavigate)
| プロパティ | 値 |
|---|---|
| Text | "" |
| Visible | false |
| OnSelect | Navigate(SearchScreen, ScreenTransition.None) |
ボタンのVisibleをfalseにしているため、ユーザーには見えません。このボタンはSelect関数から呼び出すための「トリガー役」です。
LoadingScreen.OnVisible
// 変数を初期化
Set(gblIsAdmin, false);
Set(gblImportButton, false);
Set(FormattedUserName, "");
// 権限チェック(SharePointリストを参照)
Set(
gblIsAdmin,
!IsBlank(
LookUp(
ユーザー権限管理一覧,
メールアドレス = User().Email
)
)
);
Set(gblImportButton, gblIsAdmin);
// ユーザー名を整形(姓名の順序を入れ替え)
Set(
FormattedUserName,
Last(Split(User().FullName, " ")).Value & " " &
First(Split(User().FullName, " ")).Value &
If(gblIsAdmin, "(管理者)", "")
);
// 起動ログの登録
IfError(
Patch(
起動ログ一覧,
Defaults(起動ログ一覧),
{
メールアドレス: User().Email,
ユーザー名: User().FullName
}
),
Notify(
"ログ登録に失敗しました: " & FirstError.Message,
NotificationType.Error
)
);
// 非表示ボタンを自動押下 → 検索画面へ遷移
Select(btnNavigate);
検索画面(SearchScreen)での変数利用
// ユーザー名ラベルのTextプロパティ
FormattedUserName
// 管理者専用ボタンのVisibleプロパティ
gblImportButton
LoadingScreen.OnVisibleの処理がすべて完了してからSelect(btnNavigate)が実行されるため、検索画面に遷移した瞬間にはすべての変数が確定しています。
なぜOnVisibleで直接Navigateしないのか?
LoadingScreen.OnVisibleに直接Navigate(SearchScreen)を書けば良さそうに思えますが、Power Appsには以下のような制約があります。
OnVisibleにNavigateを直接記述すると、「このスクリーンからは必ず自動的に移動するため、ここでは移動は使用できません。」というエラーが発生する場合がある
これは、OnVisible内のNavigateをPower Appsが「常に自動遷移する画面」と検知し、エラーとして扱うケースです。
非表示ボタンのSelect関数を使うことで、OnVisibleから間接的に画面遷移を実行でき、このエラーを回避できます。
// 直接遷移 → エラーになる場合がある
Navigate(SearchScreen)
// 間接遷移 → エラーを回避できる
Select(btnNavigate)
Select(btnNavigate)は、プログラムからボタンのクリックをシミュレートする関数です。ボタンのOnSelectに書かれたNavigateが実行されるため、OnVisibleから安全に画面遷移を実現できます。
まとめ
今回学んだことを整理します。
- App.Formulasは遅延評価される — 外部データソースへのアクセスを伴う処理を書くと、値が確定する前に画面が表示されてしまうことがある
- App.OnStartは非推奨の流れがある — 将来的な不具合リスクを避けるため、依存を最小限にした方が安全
-
LoadingScreen.OnVisibleに初期化処理を集約する —
Setによる同期実行で、すべての値が確定してから遷移できる -
OnVisibleで直接Navigateするとエラーになる場合がある — 非表示ボタン+
Select関数で間接的に遷移することで回避できる
| やりたいこと | 実装方法 |
|---|---|
| 起動時に読み込み中画面を表示したい | App.StartScreenにLoadingScreenを指定 |
| 初期化処理の完了後に遷移したい | OnVisibleで処理 → Select(btnNavigate) |
| OnVisibleのNavigateエラーを回避したい | 非表示ボタン+Select関数で間接遷移 |
| App.OnStartへの依存をなくしたい | 全処理をLoadingScreen.OnVisibleに移行 |