Power Apps 格闘戦
Microsoft Power Platform で頑張る。
環境は次の通り。
(Power)
- Power Automate
- Power Apps
(永続化)
- SharePoint Online
- Dataverse
20230710
#SharePoint
- サイト作る
- リスト作る
- 適当にスキーマを組む
- 頑張る
#SharePoint
「タイトル」列は消せない。非表示にはできる。
#Dataverse
データの要件的にドキュメント志向なデータベースだとすっごい話が早い。
でも Dataverse で NoSQL 的操作ができるような情報が見えてこない。Azure Cosmos DB とコネクタでつながらないとだめですか?
#PowerApps
わからないことは次の通り。
- グローバルGUIの最善慣行が見えてこない。コンポーネントでやればいいと思ったが、画面からコンポーネントを参照すると内部のコンテナ等にアクセスできないため意味がない。
- Automate とコンポーネントを束縛させる方法もよくわからない。Automateの関数名.Run()でいいならそれでいいんだけど。
- カスタムコンポーネントを作るとき、カスタムプロパティの関数だかイベントだかアクションだかの違いがわからない。なんだこれ?
#PowerApps
Patch
関数を用いることで、フォームの代わりに送信処理をSPのリストに対して発火できる。
(例)
ボタン コンポーネント SendButton
に、SharePoint リスト posts
に対するフォームの送信処理を実装します。
SendButton.onSelect = Patch(
posts,
Defaults(posts),
{
sender: "sender",
content: "aaaaaaaa"
}
)
#PowerApps
例えば実行者のユーザ名やメールアドレスを取得したいときは、次のようにできる。
User().Email
User().FullName
User().Image
これら三つのプロパティ以外は提供されない。
#SharePoint
created_at
的なメタ情報程度なら SP 側で勝手に生成してくれるようなので、こちらでスキーマとして準備する必要はない。デフォルトでは非表示になっているので表示設定が必要なことも付せて。
#PowerApps
ボタンの有効・無効化はプロパティ DisplayMode
で指定する。
(例)
ボタン コンポーネント SendButton
についてボタンを有効・無効にします。ここでは、チェックボックス コンポーネント ConfirmCheck
も実装されています。
If(
ConfirmCheck.Checked,
DisplayMode.Edit, // if true
DisplayMode.Disabled // if false
)
#与太話
見ての通り、コンポーネントに対するプロパティの指定はほぼ Excel である。オブジェクト指向と Excel の即値的パラダイムが混ざって大変気持ち悪い。
#PowerApps
チェックボックスのチェック状況は CheckBox.Checked で取得できる。
#PowerApps
モダン コントロールとクラシック コントロールについては、要件が許すならモダンにするべきだと考えられる。モダンのほうではアクセシビリティやUXがクラシックに比べて改善されている。
#与太話
要調査事項:
- 取得・表示するデータをユーザの権限によって変化させる方法。Power Apps単体ではおそらく不可能なため、Automateを用いることになるが知見がまったくない。
- ポップアップウィンドウの実装。コンポーネントで呼び出せばいいらしいが、インタフェースの操作を考えるとどの記事も詰めが甘い[1]。でも参考にはしたい。
- Dataverseのデータ格納について。データ型にJSONを許容して、しかもスキーマを設定できるなら大したものだがそこまで期待はできない。万が一リレーショナルなテーブルしか実装できないなら、SPのリストと大差はない。
-
別画面に遷移させるものに至っては、それはそもそもポップアップですらない。 ↩︎
#与太話
現在Dataverseが権限不足でアクセスできない状況に陥っている。神様助けて。
#dataverse
DataverseにJSON型自体はあるらしいが、非SQLストアの Log だか Audit って何?
#与太話
dataverse は自明に universe をもじった単語である。じゃあ data はともかく -verse って何?ってなるじゃん。なるんですよ。なって。
そもそも univese は uni- と -verse に分解できます。uni はラテン語で「1」程度の意味です。そして verse はつまるところ versus に行き着くらしいんですよ。一つに絡みつくみたいなイメージかな?
じゃあ何、-verse はそういうことなの、対立 versus ってことなの?っていうと現代英語の接辞用法的にはまた違うみたいなんです。結論から言うと、-verse は「特定の要素全体を示す」働きをする接辞になりました。だから、dataverse は「データ全体」ぐらいの意味を指す複合名詞ってわけですね。
これで今日からあなたも dataverse[1] マスター。
-
not “Dataverse” ↩︎
#PowerApps
Patch
vs Collect
Patch 関数には以下のメリット・デメリットがあります。
- 1件のデータを登録する時には結果が戻り値として帰ってくるので使いやすい
- Patch 内で起こったエラーを拾える
- 大量のデータを入れると時間がかかる or そもそもできない
- 公式のサンプルアプリでは Patch が使われている
Collect 関数には以下のメリット・デメリットがあります。
- 複数のデータを登録する時は高速
- 公式のサンプルアプリでは使われていない
- 戻り値が基本的にテーブル型で帰ってくるため、Firstなどの関数を使う必要がある
- エラーが拾いにくい
#PowerApps
リッチテキストエディタからテキストを取得するときは次のようにする。形式はHTMLで固定となる。
(例)
リッチテキストエディタ コンポーネント Editor
から入力データを取得します。
Editor.HTMLText
#PowerApps
ドロップダウンの扱いは謎。即値で選択肢をご用意いただかないで変数から注入しろっていうのはわかる。その変数はどこで定義するんですか?
#PowerApps
グローバル変数の宣言は実にフリーダムであり、適当なところで Set(name, value)
すればよいらしい。
どう考えても無秩序に陥るが、グローバル変数は App オブジェクトの OnStart で宣言すると制約すればそれで済みそう。
20230711
#PowerApps
一般的には、変数の使用を避けてください。 ただし、変数を使わないと目的の動作が得られないこともあります。
#PowerApps
Power Fx で実装される関数 Set()
は、Power Apps においては全画面共通で参照・更新できるグローバル変数と同じである。同実装上の関数 UpdateContext()
は特定画面でのみ用いられるローカル変数である。思うにこれはローカルというよりコンテキスト(文脈)と解釈したほうが見通しが良くなるかもしれない。
#PowerApps
関数 Patch
の戻り値を見て、成功の可否でダイアログを出したいな~って思いません?
#PowerApps
関数 Navigate()
は、
- 第二引数に画面遷移のエフェクト
- 第三変数に次の画面に与えるコンテキスト変数
を指定できる。
(例)
画面 コンポーネント scrMain
と scrNextPage
があります。scrMain
に実装されたボタン コンポーネント btnProceed
を押すと、scrNextPage
に遷移したうえで、コンテキスト変数 ctxEmail
と ctxPassword
を与えます。また scrMain
にはテキストボックス コンポーネント txtEmail
と txtPassword
が実装されています。
btnProceed.onSelect = Navigate(
scrNextPage,
UnCover,
{
ctxEmail: txtEmail.Text,
ctxPassword: txtPassword.Text
}
)
#PowerApps
ページ遷移は大体わかった。Back()
のページ遷移エフェクトは、来た時が Uncover
だったら自動的に UncoverRight
になったりするんですかね?
#PowerApps
Power Apps / Power Fx 両仕様にハッシュ関数の類はないという報告を受けた。マジで?
ログイン処理とか当然挟むけど、ソルトはおろかハッシュでやり取りできないなら平文でやるしかない?
#Power Apps
テキストボックスをパスワードボックス化するには次の手順を踏む。
(例)テキスト入力 コンポーネント txtPassword
を定義します。このコンポーネントに入力された文字は適切にマスクされるほか、パスワードの取り扱いに関する情報をブラウザに提供します。
txtPassword.Mode = TextMode.Password
#PowerApps
必要であれば、Power Apps Studio の左のペインにある「変数」グループ((x)
)から各画面に定義されたコンテキスト変数およびアプリ全体のグローバル変数を参照できる。
#PowerApps
テキストボックスなどの入力データを削除したいときは、関数 Reset()
を用いるとよい。
(例)
テキスト入力 コンポーネント txtUsername
に入力されたユーザ名と txtPassword
に入力されたパスワードを消去します。消去するタイミングは画面が表示されたとき、すなわち、イベント OnVisible
が発火したときです。
Reset(txtUsername);
Reset(txtPassword);
#PowerApps
同一数式内で複数の処理を(直列に)実行するときは、セミコロン ;
で命令を区切る。例は上記の通り。
#与太話
依然としてドロップダウンの選択肢候補を変数を経由して定義する方法が判明していない。
#PowerApps
SharePoints リストのデータを持ってくるには Filter 関数などを使えばよい。
#PowerApps
LookUp
と Filter
の違いはなんだろう
Filter
は長さが LookUp
は所与条件に合致した最初のレコードを返す関数、とのこと。
だから、LookUp
= First(Filter(source, { ... }))
である。
#PowerApps
SharePoint からリストを持ってきて表示するには Gallery コンポーネントが使える。選択内容を取ってくるにはオブジェクト内部(.Selected
)にあるテキストテキストコンポーネントの Text プロパティをたたけばよい。
#PowerApps
関数 Notify()
で上部に簡単なバナーを表示できる。警戒色などにもできて便利だけど、行がずれるのはちょっといただけないな。
#PowerApps
ギャラリーの Default
に与える要素(すなわちアプリを開いたときに最初に選択される項目)は LookUp()
だかで仕入れてきたレコードで OK っぽい。ギャラリーの要素の中で仕入れてきた要素に一致するものがあれば、そのギャラリー要素がデフォルトで選択されることになる。
#SharePoint
ところで SPO の列を一意にしたかったり必須にしたり型を変えたかったら、SPOリストの右上歯車→リストの設定からいい感じに設定できる。
#PowerApps
「データソースに当該データが存在しない」という表現は IsBlank()
と LookUp()
を用いればよい。
(例)
ボタン コンポーネント btnSend
にレコードの存在の有無を通知する機能を実装します。スクリーンにはテキスト入力 コンポーネント txtId
が存在しており、これにはユーザーの ID が入ります。btnSend
はユーザーの ID を基準にデータソースを走査し、存在の有無をバナーで利用者に通知します。
btnSend.OnSelect = If(
IsBlank(LookUp(source, id=txtId)),
Notify("The record doesn't exist"),
Notify("The record exists")
)
#PowerApps
いわゆる Upsert をしたい場合は、やはり Patch()
を用いるとよさそう。ただし、データが既に存在する場合と存在しない場合で切り分けて処理しないとエラーが発生するっぽい?要検証。
例の説明文を書くのがめんどくさくなってきた。
UpdateContext({
locUser: LookUp(source, id=txtId)
});
IfError(
Patch(
source,
If(
IsBlank(locUser),
Defaults(posts),
locUser
),
{
id: txtId,
contents: "..."
}
),
Notify("FAIL", NotificationType.Error),
Notify("SUCCESS", NotificationType.Success)
);
#PowerApps
Patch()
とかで失敗した場合、IfError()
などでハンドリングできる。
#PowerApps
Excel でいうところ IFS()
は Power Fx では Switch()
関数になっている。構文は驚くほどクリソツで、なんで IFS()
にしなかったのか不思議に思うぐらいには同じ。
Switch(expr, cond1, result1, [cond2, result2, [cond3, result3, [...]]], default)
としたとき、expr
は最初の一回のみ実行されたうえで、cond1
, cond2
との照合が行われる。完全一致であれば、添字が一致する result
(e.g. cond1
→ result1
)を返り値とする。一致する候補がなかったときは最後の default
を返り値とする。
#PowerApps
ギャラリーコンポーネントの要素は Items
プロパティに与える値をいじれば制御できる。
デフォルトでは gryComponent.Items = datasource
であるが、ここで datasource
を Filter
関数などに渡せば動的に表示要素を制御できる。
注意:見た目の上での制御となると考えられる。権限ベースで動いているわけではないので、やろうと思えば非表示項目もアプリ内部からデータを持ってくることは可能だと推測される。
#SharePoint
Power Apps に連携している SPO のリストがあったとして、アプリを配布したらそのリストの内容もアプリの配布先に筒抜けになっちゃうのかな?要検証。
#PowerApps
なんでか知らんが、選択肢(ドロップダウンコンポーネントだかSPOのリスト列だか)の内容は Power Apps 内部では drpComponent.Selected.Value
としなければエラーが出る。.Selected
で十分だと思うんだけど。
#PowerApps
フォームコンポーネントの扱いは非常に面倒くさいけれど、細やかな調整が不可能になった代償にかなり実装の手間を省けてとても便利ではある。
フォームコンポーネントを設置したら Dataverse なり SPO のリストなどに接続する。後は外界からボタンの OnSelect
イベントなどで所定の関数を実行すればよい。所定の関数は次の四つである。なお、すべて唯一の引数として操作対象となるフォームのオブジェクトを受け取る。
-
NewForm
- 対象フォームを新規入力ができる形で初期化する。 -
EditForm
- 対象フォームのItem
プロパティについて編集ができる形で初期化する。 -
ViewForm
- 〃について閲覧のみできる形で初期化する。 -
ResetForm
- 対象フォームをまっさらな形に初期化する。
#PowerApps
フォームに見出しなどを追加したい場合は、「カスタムカード」を代理に挿入したうえで、カスタムカード内にラベルなどを設置する。この場合レスポンシブ配置は作動しないので、レイアウトは完全に手動である。なんだこれ?
#Dataverse
#Dataverse
Unique Constraint は効かないっぽいので、代替キー(Alternative Key)なるものを用いなければならないかも。
テーブルの概要を開いて、スキーマの「キー」→「新しいキー」であとは適当に列を選択すれば代替キーが出来上がる。複合キーにしたいなら列を複数選択する。
このときキー名は何でもいいっぽいけど、pk とか ak とかそんなふうにしたほうがいいんだろうか。
#Dataverse
今日はテーブルのスキーマを考えて終わりだった。以下、Dataverseで読む資料。
#dataverse
1件の最小セットをDVにインポートさせると、なんと3000件完全に空であるレコードがついでに挿入される。二回やると8000件、三回目は1.3万件・・・ひどい・・・
#dataverse
選択肢を型として宣言できるんだけど、選択肢を機械的に注入する方法がない。
そのせいで、300個ぐらいの選択肢がある列を作るとき、300個ぐらいの選択肢をスキーマを作るときにチマチマ入れていかないといけない。非現実的だ。
SPOだと概ねうまくいく。SPOも機械的に注入する方法はないが、テキストボックスに行単位で候補を入れるだけなのでだいぶ気が楽。
#PowerApps
モダンコントロールはバグっていて、クラシックなほうで動く挙動がモダンだと動かない例を発見した。
(事例)
クラシック・モダン両方のドロップダウンコントロールを用意して、Items = Distinct(source, row)
とする。クラシックなほうは列名もとい選択値が表示されるが、モダンなほうは選択肢が表示されないときがある。外部からテキストラベル経由で選択値を持ってくると正当なデータが返ってくるため取得自体はうまくいっているが、レンダリングがダメ。
#PowerApps
テーブルとレコードと配列宣言の違いがいまだによくわからない。Power Fx は配列すら宣言できないように見えるし、配列で渡せばいいところをわざわざテーブル(多分連想配列)で渡さないといけなかったりして非常に保守性が悪い。
#PowerApps
テキストボックスの字数制限は OnChange
およびに Default
プロパティに以下のような処理をいれれば行ける?
(OnChange)
UpdateContext(
{
var10:
Left(
Self.Text,
10
)
}
);
Reset(Self)
(Default)
var10
#PowerApps
モダンコントロールでもいつの間にテーマ制御ができるようになっていた。
細かい色の指定は無理だが、色調を要件に寄せるぐらいのことはできる。