OnVisibleに「何を置くか」は整理できた。さらに「処理をまとめる」余地はどこにある?
Part 4では、データのライフサイクルに沿って OnVisible に書くもの/書かないもの を切り分ける話をしました。続く「置く場所シリーズ」として、今回は 呼び出し可能な処理ブロックをどこに置けるか にフォーカスします。
Power Appsで開発を続けていて、こんなことに困ったりしました。
「OnVisibleが長くなってきた。まとまった処理をどこかに分けたい」
画面に入った時に走らせたい処理は、開発が進むにつれてどんどん増えていきます。データの初期化、コレクションの再構築、UIの状態リセット、Galleryの選択行クリア……。気づけばOnVisibleが何十行にもなって、何が書いてあるか自分でも追いきれなくなったり。
この記事は、そんな時の「分け先のひとつ」として、Button.OnSelectを"呼び出し可能な処理ブロック"として活用する方法について考えたいと思います。Part 1〜3では Screen.OnVisible の整理や Navigation Context、Execution Plan を扱い、Part 4では宣言と画面表示時の境界を扱いました。今回はもうひとつの選択肢としての Button.OnSelect と Select() の話です。
この記事で伝えたいこと
-
Select()を使うと、Button.OnSelect を「名前付きで呼び出せる処理ブロック」として扱える - ユーザー定義関数が GA した今でも、画面ローカル・Navigate・UI 直接参照など Button.OnSelect + Select() にしかできない住み分けがある
-
Select()は完了検知できない(fire-and-forget)が、(a)〜(d) の条件に当てはまれば問題にならない - Loading 画面の自動遷移のように、構造的にこの手法が必然になる例がある
前提
- Power Apps のCanvasアプリ
- Part 4 までの流れを踏まえ、「宣言・初期化」とは別軸で、画面内の手続きをどこに置くかを扱う
- AIと協働しながら、より良い設計パターンを試行錯誤中
Button.OnSelectは Select() で外から呼べる
まず基本の確認から。
Button.OnSelectに書いた処理は、ボタンをクリックした時に実行されます。ただ、Power Fxには Select() という関数があり、これを使うと他の場所から任意のボタンの OnSelect を発火させることができます。
// btnApplyFilter.OnSelect に絞込み処理を書いておく
ClearCollect(colResult,
Filter(Products, Category = drpCategory.Selected.Value)
);
// 別のコントロールから呼び出す
// drpCategory.OnChange:=
Select(btnApplyFilter);
これで、ドロップダウンを変更した時にも検索ボタンを押した時と同じ処理が走ります。
つまり Button.OnSelect は、「ボタンに紐づく処理」というだけでなく、「名前付きで呼び出せる処理ブロックの置き場所」 としても使えるわけです。
「ユーザー定義関数がGAしてますよね?」への答え
ここで多くの人が思うはずです。「いや、それユーザー定義関数(User Defined Function)でよくない?」
その通りで、2025年9月にユーザー定義関数は正式リリース(GA)されています。しかも behavior UDF として、Set や Collect などの副作用を含む関数も書けます。なので「処理を再利用したい」という動機の大部分は、本来UDFで解決できる時代になりました。
ではなぜ今、Button.OnSelect + Select() の話をするのか。両者には住み分けがあるからです。
| UDF | Button.OnSelect + Select() | |
|---|---|---|
| 置き場所 | App.Formulas(アプリ全体) | 画面(ローカル) |
| パラメータ | あり | なし |
| 戻り値 | あり | なし |
| Navigate | 不可 | 可 |
| UIコントロール参照 | 引数で渡す必要 | 直接参照できる |
つまり、ユーザー定義関数はアプリ全体の「関数」、Button.OnSelectは画面ローカルの「手続き」。少し性格が違います。
特に Button.OnSelect が今でも残る固有の価値は次の3つです。
- 画面ローカル:その画面に閉じる処理を、その画面に置いておける(コロケーション)
- 画面遷移を扱える:Navigateを含められる
- 画面のUIコントロールを直接参照できる:引数で渡す必要がない
書く場所が分散しがちな Power Apps の仕様だからこそ、この細かな違いが気になる局面はあります。
Select()での留意点:終了検知できない
Select(btnXxx) を呼ぶと、btnXxxのOnSelectは走り始めはするのですが、呼び出した側はその完了を待ちません。いわゆる投げっぱなしです。
Select(btnLoadData); // データ読み込み開始
Select(btnRender); // ↑の完了を待たずに次が走る
例えばこのようにした場合、「処理1が終わったら処理2、その結果を見て処理3」みたいな順序保証や戻り値が必要な処理は、Select()では書けません。1つのOnSelectへまとめるなどの判断や工夫が必要です。
逆に言うと、この制約に引っかからない使い方であれば、Select()は問題なく使えるということでもあります。
それでも有効に使える4つの条件
「終了検知できない」という制約があっても、以下のどれかに該当する処理ならSelect()で十分に機能します。
(a) 後続処理がない、または独立している
検索の再実行、通知表示、リフレッシュなど「やって終わり」の処理。呼び出し側に続きの処理がない場合は、完了を検知する必要がそもそもありません。
(b) 結果がリアクティブに伝播する
Select(btnApplyFilter) が走って colResult が更新されれば、それを参照しているGalleryは自動的に再描画されます。Power Appsのリアクティブな仕組みに乗っかれるなら、明示的な完了検知は不要です。
(c) ユーザー入力がトリガー
ユーザーがボタンを押す → 結果が画面に表示される → 次のアクションもユーザーが起こす。連鎖の主体が人間なので、プログラムが完了を待つ必要がありません。
(d) 画面遷移で完結する
Navigateで終わる処理なら、後続は遷移先の画面で勝手に走ります。Navigateの完了を待つ意味がそもそもない設計です。
代表ユースケース:Loading画面の自動遷移
ここまでの話を踏まえると、Select(Button)が代替不可能な1つのケースとして、Loading画面の自動遷移が挙げられます。
Power AppsでLoading画面を作るとき、こういう構造になったりします。
ローディング画面(LoadingScreen)構成
[LoadingScreen]
├─ Spinner: Spinner_Loading
└─ Button: Button_GoToNext (Visible: false)
LoadingScreen.OnVisible
// LoadingScreen.OnVisible
// 必要な初期化処理
ClearCollect(colMaster, MasterTable);
...
Select(Button_GoToNext); // 必要な処理を実行した後ButtonのOnSelectを呼び出す
Button_GoToNext.OnSelect
// Button_GoToNext.OnSelect
// 必要な処理を実行した後次の画面に遷移
Navigate(MainScreen);
なぜこれが必要かというと、Screen.OnVisibleではNavigate関数が使えないからです。
あえて待機画面を用意して、時間のかかる処理を確実にするためにユーザーを待たせ、完了したら自動的に次の画面に遷移させるためには現状この方法が基本です。
まとめ
Button.OnSelectは、ボタンクリックの行き先であるだけでなく、画面ローカルな「呼び出し可能な処理ブロック」の置き場所としても機能します。
- ユーザー定義関数がGAした今でも、画面ローカルな処理・画面遷移を含む処理・UIコントロールを直接参照する処理においては、Button.OnSelect + Select() のアプローチを選択肢として考えても良さそう
- 制約は「完了検知できない」こと
- ただし(a)〜(d)のいずれかに該当する処理であれば、その制約は問題にならない
- Loading画面の自動遷移は、構造的にこの手法しか取れない代表例
「OnVisibleが長くなってきた」と感じたら、分け先のひとつとして、Button.OnSelectを思い出してみてください。
シリーズ一覧
- その1:画面のことは画面自身に任せる
- その2:Navigation Contextパターン
- その3:Execution Planパターン
- その4:OnVisibleに全部置かない。データのライフサイクルで配置を決める
- その5:Button.OnSelectを「呼び出し可能な処理ブロック」の置き場所として使う(本記事)