(2019/08/13)
ユーザー列のフィルタリングが委任可能になったことに伴い、本記事の以下箇所の記載を微修正しました。
ケース4:動的にフィルタ処理したい
の 改善2:登録者のフィルタ処理の記述を簡素化する
委任シリーズ第3回目です。本記事をご覧になりたい方は、先に前回の記事をご覧いただくと話がスムーズです↓
PowerAppsで遭遇する5つの委任問題とちょっと強引な回避方法(SharePointリスト利用時)その1
PowerAppsで遭遇する5つの委任問題とちょっと強引な回避方法(SharePointリスト利用時)その2
最終回(?)となるその3では、列の種類に関わらずそこそこ委任問題に遭遇した以下のケース
ケース4:動的にフィルタ処理したい
ケース5:動的にソート処理したい
について、私が実際に行っている、委任問題のちょっと強引な回避方法について紹介します。
紹介で利用するデータソース
ここでは、以下のSharePointリスト「テストリスト」について考えます。
※列の種類は、前回(その2)の記事の作業終了時点の状態です。
#ケース4:動的にフィルタ処理したい
例えばステータスで、
「すべてのステータス」
「"新規"ステータスのみ」
「"新規"ステータス以外」
をユーザー操作で切り替えて表示する、などの需要は結構あるかと思います。
ということで早速、数値列"ステータス"に対して、上記のフィルタ処理を切り替える機能を追加してみます。
こんなイメージ↓
まず、ラジオボタンを追加し、Itemsに以下を記載しておきます。
["すべてのステータス", "新規ステータスのみ", "新規ステータス以外"]
そして、ラジオボタンの選択に応じてフィルタを変える処理をGalleryに書いてみます。
フィルタの条件を変えるのだから、Filter関数内で分岐すればいいよね…
※ "すべてのステータス"の時も何かしら処理を書かないとFilter関数の書式を満たせないので、"すべてのステータス"の時は全ての値を網羅できるよう「ステータス >= 1」と書いている
Filter(テストリスト,
Switch(Radio1.Selected.Value,
"すべてのステータス", ステータス >= 1,
"新規ステータスのみ", ステータス = 1,
"新規ステータス以外", ステータス <> 1
)
)
委任警告さん、しつこいね。
どうやらFilter関数内の分岐処理が委任できないようです。
Switch文の代わりにIf文を使用しても委任警告に出会うため、私が知る限り、SharePointではフィルタ内の条件分岐は委任できません。
回避方法
委任警告の回避方法自体は単純で、条件分岐をFilter関数の外に出せば解決します。
Switch(Radio1.Selected.Value,
"すべてのステータス", Filter(テストリスト, ステータス >= 1),
"新規ステータスのみ", Filter(テストリスト, ステータス = 1),
"新規ステータス以外", Filter(テストリスト, ステータス <> 1)
)
委任警告が消えました。めでたしめでたし。
PowerApps側から見ればどちらも結果的には同じ処理に見えますが、SharePoint側から見ると両者は異なるため、このような違いが生まれます。
イメージ図↓
ただこの方法だと、フィルタ条件が増えれば増えるほどGalleryのItemsに記載するFilter関数の数が肥大化します。
例えば上記に加えて登録者が"全員"と"本人のみ"を切り替える機能を追加するには、ステータス3条件×登録者2条件=6個のFilter関数が必要です。
物理的には可能ですが、Itemsの可視性が失われ、修正が困難になってしまいます。
そこで、フィルタ条件が増えてもいいように、フィルタ処理の記載を簡素化する方法について考えてみることにしました。
###改善1:ステータスのフィルタ処理の記述を簡素化する
ここで話は前回の記事(ケース3)のまとめに戻ります。
ステータス名を保持したいのなら、なぜ"新規"などのステータス名をそのままテキスト列に保存せずに、わざわざ数値列を使って面倒な処理にしたの??
実はこれには目的がありまして、
「すべてのステータス」
「"新規"ステータスのみ」
「"新規"ステータス以外」
などの様々な条件を、**"なるべくフィルタ処理の記述を簡単に"**実装するために、数値列を使用しました。
(゚Д゚)ハァ???
早速やってみます。
"新規"などのステータスは、数値列に数値として保存しています。
再掲↓
値 | ステータス |
---|---|
1 | 新規 |
2 | 対応中 |
3 | 完了 |
まず、AppのOnStart処理で、ステータス値の範囲を指定する2つの変数:LowerとUpperを以下のように初期化します。
Set(Lower, 1);
Set(Upper, 3);
追記したら、OnStartの実行をお忘れなく。
続いて、最初に追加したラジオボタンのOnChangeで、ラジオボタンの選択に応じて上記の変数を以下のように更新させます。
Switch(Radio1.Selected.Value,
"すべてのステータス",
Set(Lower, 1); Set(Upper, 3),
"新規ステータスのみ",
Set(Lower, 1); Set(Upper, 1),
"新規ステータス以外",
Set(Lower, 2); Set(Upper, 3)
)
最後に、Galleryのフィルタ処理を以下のように書き換えて完了です。
Filter(テストリスト,
ステータス >= Lower, ステータス <= Upper
)
このフィルタ処理では、ステータスの範囲を以下のように変数で定義しています。
Lower <= ステータス <= Upper
この考え方なら、
"すべてのステータス"なら、Lower=1、Upper=3 としてステータスの範囲を1~3まで広げる
"新規ステータスのみ"なら、Lower=1、Upper=1 としてステータスの範囲を実質[ステータス=1]とする
というフィルタ処理が、条件分岐せずに可能になります。
この考え方は日付範囲のフィルタリングにも利用できますので、処理したいフィルタの項目が増えても、フィルタの条件式を何個も分岐して記載する負担は大きく軽減されるかと思います。
ステータスが1または3の時はどうすんの?
あー。。
今回のようにステータスが3つであれば、Lower=3, Upper=1とすれば強引にできますが(笑)、ステータスが4つ以上で"2または4の時"などは表現できないので、場合によっては条件分岐が必要になるかもしれませんm(_ _)m
###改善2:登録者のフィルタ処理の記述を簡素化する
ここで話は前々回の記事(ケース1:人のフィルタリング)に戻ります。
ケース1の完成イメージでは、Galleryのフィルタ処理が2行必要だったため、これも1行にまとめます。
先ほどの改善1のステータスフィルタ処理に、登録者のフィルタ処理を加えていきます。
まず、登録者のフィルタ処理を切り替えるためのスイッチを用意します。
続いて、スイッチのチェックを入れた時とチェックを外した時に変数を更新する処理を記載します。
チェックを入れた時(本人のみ表示)は変数に本人のEmailを格納し、チェックを外した時(全員分表示)は変数の中身を空にしています。
Set(UserEmail, User().Email)
Set(UserEmail, "")
最後に、Galleryのフィルタ処理を以下のように書き換えて完了です。
Filter(テストリスト,
ステータス >= Lower, ステータス <= Upper,
StartsWith(登録者, UserEmail)
)
StartsWith関数は、あるテキスト文字列が別のテキスト文字列で始まるかどうかを確認するもので、ここでは登録者メールアドレスが変数UserEmailで始まるかどうかをチェックしています。
この記載だけで「全員分表示」も処理が可能なのは、StartsWith関数が「検索するテキストが空の文字列の場合、StartsWith は true を返す」仕様であるためです。
ちなみにこのStartsWith関数は、アプリをデータソースから自動生成した際にGallery処理に最初から備わっているので、仕様含めご存知の方も多いかと思います。
参考:PowerApps の EndsWith 関数と StartsWith 関数
完成!
以上で、「ケース4:動的にフィルタ処理したい」は完成です。
ステータスと登録者の6条件のフィルタ処理が、たった1つのFilter関数で実現できました。
#ケース5:動的にソート処理したい
例えば、
「日付順」
「ステータス順」
をユーザー操作で切り替えて表示する、などの需要は結構あるかと思います。
ということで早速、ソートする列を切り替える機能を追加してみます。
なお、昇順/降順の切り替え処理は、アプリをデータソースから自動生成した際にGallery処理に最初から備わっているので、ここでは説明を割愛しすべて昇順(Ascending)固定で説明します。
まず、ラジオボタンを追加し、Itemsに以下を記載しておきます。
["日付順", "ステータス順"]
そして、ラジオボタンの選択に応じてソート対象列を変える処理をGalleryに書いてみます。
ケース4で完成したフィルタ処理を、ソート関数SortByColumnsで包みました。
("CreatedValue"は登録日時数値列、"Status"はステータス列の列名です)
ソート条件を変えるのだから、こんな感じでSortByColumns関数内で列の指定部分を分岐すればいいよね…
SortByColumns(
Filter(テストリスト, ステータス >= Lower, ステータス <= Upper, StartsWith(登録者メールアドレス, UserEmail)),
If(Radio2.Selected.Value = "日付順", "CreatedValue", "Status"),
Ascending
)
委任警告さん、もうやめて!!!
どうやらSortByColumns関数内の分岐処理が委任できないようです。
残念ながら、分岐処理の代わりに列名が格納された変数に置き換えても、委任警告に出会いました。
よって、私が知る限り、SharePointではソート列の動的な指定は委任できません。
回避方法
委任警告の回避方法自体は単純で、条件分岐をSortByColumns関数の外に出せば解決します。
If(Radio2.Selected.Value = "日付順",
SortByColumns(Filter(テストリスト, ステータス >= Lower, ステータス <= Upper, StartsWith(登録者メールアドレス, UserEmail)), "CreatedValue", Ascending),
SortByColumns(Filter(テストリスト, ステータス >= Lower, ステータス <= Upper, StartsWith(登録者メールアドレス, UserEmail)), "Status", Ascending)
)
…って、おい!!
それじゃケース4の途中と同じで可視性が失われるじゃねぇか!それだけじゃ"回避"とは呼ばせねぇぞ!!
ごめんなさい。。
私には、**"SharePoint側"**にソート列の動的な指定を委任する方法や驚くべき回避方法は発見できませんでした。
**"SharePoint側"**には委任できない、ならいっそのこと…
ということで、最終手段を取ります。
###最終手段:フィルタ処理結果を都度Collectionに格納し、Collectionに対して動的ソートを行う
先ほどのSortByColumns関数内でのソート列の動的な指定で委任警告が出たというのは、つまり
「ソート列の動的な指定を"SharePoint側"に委任できなかった」
ということになります。
逆に言えば、これまでケース1~4まで行ってきたフィルタ処理はなんとか委任警告を回避できたのだから、フィルタ処理とソート処理を分けて考え、フィルタ処理はSharePointに委任し、ソート処理は自分でやればよいのではないか!
ということで、フィルタ処理結果を一旦変数に格納し、PowerApps側でその変数に対してソート処理してみることにしました。
早速やってみます。
まず、AppのOnStart処理で、フィルタ処理結果をCollection:DataListに格納します。
(登録者フィルタで使用する変数UserEmailを初期化していなかったので、ついでに追記)
Set(UserEmail, "");
ClearCollect(DataList,
Filter(テストリスト,ステータス>=Lower,ステータス<=Upper,StartsWith(登録者メールアドレス,UserEmail))
);
追記したら、OnStartの実行をお忘れなく。
これだけだと、アプリ起動時にしかSharePoint側からデータを取得できないので、これまで追加したフィルタ処理の切り替えコントロール(ラジオボタンや切り替えスイッチ)を変更する際にも都度SharePoint側からデータを取得するように処理を追記します。
各コントロールに同じ処理を書いてもよいのですが、煩雑になるので、再読み込みボタンに処理を集約します。
再読み込みボタンのOnSelect処理を以下に書き換えます。
(AppのOnStartのClearCollect処理と同じです)
ClearCollect(DataList,
Filter(テストリスト,ステータス>=Lower,ステータス<=Upper,StartsWith(登録者メールアドレス,UserEmail))
);
続いて、フィルタ処理の切り替えコントロールの変更時に再読み込みボタンの処理が発動するよう、Select処理を追記します。
ステータスフィルタ処理用のラジオボタン↓
Select(IconRefresh1);
// 追記する1行前の処理の末尾に ; がないとエラーになるので注意
登録者フィルタ処理用の切り替えスイッチ↓
Select(IconRefresh1);
// 追記する1行前の処理の末尾に ; がないとエラーになるので注意
最後に、Galleryのフィルタ処理を以下のように書き換えます。
(SortByColumnsの第一引数をDataListに置き換えただけ)
SortByColumns(DataList,
If(Radio2.Selected.Value = "日付順", "CreatedValue", "Status"),
Ascending
)
完成!
ってあれ?なんかエラーが出てる。
DetailScreenのアイテム削除処理でエラーが発生しています。
これは、「SharePointのテストリストから、Galleryで選択されているアイテムを削除する」というRemove処理です。
先ほどGalleryのItemsをSharePointリストからコレクションDataListに変更したことで、「SharePointのテストリスト」と「コレクションDataList」の間でレコードの互換性が取れなくなり、SharePoint側が「Galleryで選択されているアイテム」を認識できなくなったことが原因です。
エラーのイメージ↓
仕方ないので、無理やりレコードを繋ぎます。
IconDeleteのOnSelect処理を以下に書き換えます。
Remove(テストリスト, LookUp(テストリスト, ID = BrowseGallery1.Selected.ID));
If (IsEmpty(Errors(テストリスト, LookUp(テストリスト, ID = BrowseGallery1.Selected.ID))), Back())
完成!
以上で、「ケース5:動的にソート処理したい」は完成です。
#まとめ
Galleryを動的にフィルタ&ソート処理するために、最終的にSharePointへの委任を"諦める"という、強引な方法というか一部妥協する方法についてご紹介しました。
これまで3回に分けてご紹介した委任回避方法ですが、ここまでやればSharePointリストに対するフィルタ処理の大半は委任警告を回避できるかと思います。
(あくまで私の知見であり、動作保証はできないことをご了承ください)
ただ、回避したことでGallery以外の処理が複雑化していることも事実ですので、どこまでPowerAppsで作り込むかは微妙なところかもしれません。
委任に悩むすべての方へ、少しでもご参考になれば幸いです。
もっといい方法をご存知の方は、是非教えてください!