- 記事一覧 >
- ブログ記事
Power Appsキャンバスアプリ 【続】最初から知っていたら嬉しかった15のこと
はじめに
人柱的に社内で誰もやったことがない Power Apps のキャンバスアプリ開発をしていて、「あれ?なんで?うーん...あっ!そうか!」ってなったことが何回かありました。
そうなったとき、思ったのが、「事前に先輩とかに教えてもらっていて、知っていたら、嬉しかったなぁ。」です。
そう思った事柄について、15個手短に列挙していきます。
...というのは、前回記事、「Power Apps キャンバスアプリ 最初から知っていたら嬉しかった15のこと」の書き出しだったのですが、
今回は、前回記事に追加で取り上げたいこと15個になります。
今回取り上げた事柄は、完全に独断と偏見で選抜しています。あるあるランキングでもなんでもないです。単に初心者向けと高を括って勉強不足のまま始めたという側面もあります。
2023 年 4 月現在の Power Apps の話です。状況が異なる可能性があります。
全て Windows の Chrome 前提です。
Concatenate 関数
エクセルで同名関数がありますが、&
しか知らなかったため、何て読むんだ??から始まり、関わりたくない関数に見えました。
なんてことなく、Concatenate(コンカチネート)関数は文字列を連結する関数でした。&
演算子と同じ意味ですが、Concatenate 関数は、複数の文字を繋げられます。Concatenate("Hello", " ", "world!") // 結果: "Hello world!"
"Hello" & " world!" // 結果: "Hello world!"
&
がたくさん必要なときは、Concatenate 関数を使うと &
だらけにならないで済みます。
&
だらけにならないで済む方法として、前回記事で書いた $"..."
形式でも良いです。
$"..."
形式は、「文字列補間」と言うようです。他の言語で言う テンプレートリテラル、Template literal、テンプレート文字列、文字列展開、式展開 です。参考:https://learn.microsoft.com/ja-jp/power-platform/power-fx/data-types
結局、文字列を連結する方法として、Concatenate、&
、$"..."
の3パターンあるということになります。
データソース反映タイミング
データソースの変更は、編集中に反映されません。
再生ボタンを押しても反映されません。
ボタンの OnSelect でReset(Gallery1)
のようにしても反映されません。
「最新の状態に更新」でデータソースの再読み込みが必要です。
↓
↓
↓
↓
再読み込みが必要なデータソースがたくさんある時は、ブラウザの再読み込み(F5 キー)を行うと全てのデータソースについて反映されるようです。(注意:保存するのを忘れないでください。)
xxx=Blank() と IsBlank(xxx) では意味が違う
「xxx
が空の文字列(""
)かどうか?」を If 文に書くとき、If( xxx=Blank(), ...
ではなく、IsBlank
を使わないといけないです。
If( xxx=Blank(), ...
の xxx=Blank()
部分は、xxx
が、Blank()
(空白値) かどうかを検査しています。Set(xxx, Blank())
で xxx
の値がセットされているときは、true
です。Set(xxx, "")
で xxx
の値がセットされているときは、false
です。
If( IsBlank(xxx), ...
の IsBlank(xxx)
部分は、xxx
が、空の文字列(""
)または、Blank()
(空白値) かどうかを検査しています。Set(xxx, Blank())
で xxx
の値がセットされているときは、true
です。Set(xxx, "")
で xxx
の値がセットされているときも true
です。
なんとなく、空白の意味になりそうなSet(xxx, false)
やSet(xxx, 0)
で xxx
の値がセットされているとき、IsBlank(xxx)
は、 false
です。(true
かなと思いましたが、普通に false
でした。)
参考:https://learn.microsoft.com/ja-jp/power-platform/power-fx/reference/function-isblank-isempty
自動フォーマット
Power Apps Studio(編集画面)には、自動フォーマット(整形/Beautifier)機能があります。
式の編集中に テキストの書式設定 をクリックすると、きれいになります。
Set(グローバル変数,1);
ClearCollect(col_Innu,[{ID:1,Namae:"ポチ"},{ID:2,Namae:"むぎ"}]);
Set(VehicleType,{Car:1,Bus:2,Train:3});
↓
Set(
グローバル変数,
1
);
ClearCollect(
col_Innu,
[
{
ID: 1,
Namae: "ポチ"
},
{
ID: 2,
Namae: "むぎ"
}
]
);
Set(
VehicleType,
{
Car: 1,
Bus: 2,
Train: 3
}
);
フォーマットの解除 をクリックすると、元に戻ります。
と思ったら、元の形とは違う形になりました。フォーマットと逆のロジックが働いているようです。(元々の形が重要だった場合、戻せなくなる可能性があります。)
ActiveDirectory から取れる情報
Office365ユーザー.MyProfileV2()
で取り出せる情報を調べてみました。
調べ方は、単純で、Set(userV2,Office365ユーザー.MyProfileV2());
を実行させて、グローバル変数 userV2
の中身を目視しました。
「Azure AD の項目」とは、Azure ポータル → Azure Active Directory → ユーザー に表示される設定項目のことです。
●Office365 ユーザー.MyProfile()
戻り値の型:レコード型
項目 | Azure AD の項目/意味 | 値の例 |
---|---|---|
AccountEnabled | 有効なアカウント | True |
BusineessPhones | 勤務先の電話番号 | 080-0000-0000(レコード) |
City | 市区町村 | 豊田市 ○○ 町 |
CompanyName | 会社名 | 株式会社 ○○ |
Country | 国または地域 | 日本 |
Department | 部署 | 経営企画部 |
DisplayName | 表示名 | 山田 太郎 |
GivenName | 名 | 太郎 |
Id | オブジェクト ID | 00000000-abcd-1111-ef12-222222222222 |
JobTitle | 役職 | 室長 |
メール | yamada.taro@contoso.com | |
MailNickName | メール ニックネーム | yamada.taro |
OfficeLocation | 勤務先所在地 | 豊田市 ○○ 町 |
PostalCode | 郵便番号 | 000-0000 |
Surname | 姓 | 山田 |
TelephoneNumber | 携帯電話番号 | 090-0000-0000 |
UserPrincipalName | ユーザー プリンシパル名 | yamada.taro@contoso.onmicrosoft.com |
mobilePhone | 携帯電話番号 | 090-0000-0000 |
●Office365 ユーザー.MyProfileV2()
戻り値の型:レコード型
項目 | Azure AD の項目/意味 | 値の例 |
---|---|---|
abountMe | 自己紹介文※ | よろしくお願いします。 |
accountEnabled | 有効なアカウント | True |
birthday | 誕生日※ | 1/01/01 17:18 |
busineessPhones | 勤務先の電話番号 | 080-0000-0000(レコード) |
city | 市区町村 | 豊田市 ○○ 町 |
companyName | 会社名 | 株式会社 ○○ |
country | 国または地域 | 日本 |
department | 部署 | 経営企画部 |
displayName | 表示名 | 山田 太郎 |
givenName | 名 | 太郎 |
hireDate | 雇用された日※ | 1/01/01 17:18 |
id | オブジェクト ID | 00000000-abcd-1111-ef12-222222222222 |
interests | 関心事一覧※ | 映画(レコード) |
jobTitle | 役職 | 室長 |
メール | yamada.taro@contoso.com | |
mailNickName | メール ニックネーム | yamada.taro |
mobilePhone | 携帯電話番号 | 090-0000-0000 |
myStie | 個人用サイトの URL※ | https://xxxxx-my.sharepoint.com/personal/... |
officeLocation | 勤務先所在地 | 豊田市 ○○ 町 |
pastProjects | 過去のプロジェクト | ビッグプロジェクト(レコード) |
postalCode | 郵便番号 | 000-0000 |
preferredLanguage | 言語 | ja-JP |
preferredName | 呼ばれたい名前※ | やまだ |
responsibilities | 責任の範囲※ | 経営計画(レコード) |
schools | 在籍した学校※ | スタンフォード(レコード) |
skills | スキル※ | FP(レコード) |
state | 都道府県 | 愛知県 |
streetAddress | 番地 | 1-11-1 |
surname | 姓 | 山田 |
userPrincipalName | ユーザー プリンシパル名 | yamada.taro@contoso.onmicrosoft.com |
userType | ユーザーの種類 | Member |
(レコード) のところは、レコード型で、複数値を登録できます。
※ の項目は、Azure ポータル → Azure Active Directory → ユーザー に表示されませんでした。Microsoft Graph API で書き換える必要があるかもしれません。
myStie は、SharePoint の「自分のファイル」の URL が入っていました。
preferredLanguage は、
ja-JP
が入っていました。
「従業員 ID」
「従業員の種類」
は、Azure AD にあるのですが、Office365 ユーザー.MyProfileV2()
で参照できない情報のようです。
下記参考サイトは、V2 ではなく、V1 の情報のようです。
参考:https://learn.microsoft.com/ja-jp/power-apps/maker/canvas-apps/connections/connection-office365-users
部品を画面外に配置
フォームの OnSuccess、OnFailure、SubmitForm
を利用したくて、フォームを導入したけど、画面には表示したくないときがありました。(SubmitForm
の データ内容は、フォーム内の DataCard の Default、Update プロパティを直接操作して対応しました。)
Visible を false
に切り替えれば良いですが、たまに フォーム内の DataCard を直接クリックしたくなるとします。
その場合、座標に数式を書くと、外に追いやることができます。
数値だけの場合、中に留まる座標になります。(自動的に中に留まる座標に変換されます。)
外に追いやって、追いやった部分がキャンバス周囲の灰色の部分よりもさらに外の場合、クリックできなくなります。スクロールできないため、クリックするためには、縮小する必要がありました。
参考:https://qiita.com/h-nagao/items/6870610d23a18305b9d9
チェックボックス 押したときの枠を消す
チェックボックスにチェックを入れると、枠線が表示されます。
チェックを外したときでもフォーカスが当たると枠線が表示されます。
HTML の場合、チェックボックスは、そうなっていないのですが、Power Apps ではこのような初期動作になっています。
Color.Transparent
を設定して、自分で枠線が表示されないようにしないといけないです。
参考:https://anko7793.com/2022/02/524/
コレクション 先頭に挿入
レコードをコレクションの末尾ではなく、先頭に入れたいとします。
既存レコードを colIceCream
とします。
味 | 量 |
---|---|
チョコレート | 100 |
バニラ | 200 |
入れたいレコードは、{味: "ピスタチオ", 量: 50}
(1レコード)とします。
味 | 量 |
---|---|
ピスタチオ | 50 |
まず、末尾の場合、当然ですが、Collect
関数一発です。
Collect(colIceCream, {味: "ピスタチオ", 量: 50})
結果、以下のようになります。
味 | 量 |
---|---|
チョコレート | 100 |
バニラ | 200 |
ピスタチオ | 50 |
先頭の場合、少しやっかいで、以下の手順です。
ClearCollect(colTemp, {味: "ピスタチオ", 量: 50});//一旦テンポラリコレクションに入れたいレコードを格納
Collect(colTemp, colIceCream);//既存コレクションをテンポラリコレクションに追加
Clear(colIceCream);//既存コレクションをクリア
Collect(colIceCream, colTemp);//テンポラリコレクションを既存コレクション(空)に追加
Clear(colTemp);//テンポラリコレクションをクリア
結果、以下のようになります。
味 | 量 |
---|---|
ピスタチオ | 50 |
チョコレート | 100 |
バニラ | 200 |
変数に部品やコレクションそのものを代入
変数に部品やコレクションそのものを入れると、参照の意味になります。
極端な例の場合、以下のようにすると、意図したことにならないかもしれません。
//ここで、トグル切り替え前の状態を保存したつもりとする。
//しかし、変数 loc_SaveToggle は、切り替え(トグルスイッチ)Toggle1 そのもの。
UpdateContext({loc_SaveToggle: Toggle1});
//変数 loc_ToggleState は、true -> false, false -> true 反転。
UpdateContext({loc_ToggleState: !loc_ToggleState});
//ここで、トグル切り替え前の状態を読み込んだつもりとする。
UpdateContext({BeforeToggleValue: loc_SaveToggle.Value});
//BeforeToggleValueは、true -> false, false -> true 反転していて、
//切り替え後の状態になっている。
//意図したこと:BeforeToggleValueに切り替え前のトグル値を読み込み。
//実際:BeforeToggleValueに今のトグルの値が読み込まれる。
簡潔で分かりやすい例が思い浮かばなかったのですが、フォームでチェックボックスの状態を変更する→『変更前の状態』をメール送信のようなことやっていたときに、ハマりました。
以下のように
UpdateContext({loc_SaveToggle: Toggle1});
を
UpdateContext({loc_SaveToggle: Toggle1.Value});
に修正して、
.Value
で値を代入すると、意図したとおりに動きます。
UpdateContext({loc_SaveToggle: Toggle1.Value});
UpdateContext({loc_ToggleState: !loc_ToggleState});
UpdateContext({BeforeToggleValue: loc_SaveToggle});
●修正前
ラベルには、直前のトグル値(現在のトグル値の逆)を表示する趣旨ですが、現在のトグル値が表示されてしまっています。
●修正後
ラベルには、直前のトグル値(現在のトグル値の逆)が表示されています。
ラベルのマウスカーソル
ラベルに OnSelect を設定して何らかの処理をするとします。クリックすると、何らかの処理を行うラベルになりますが、マウスカーソルをラベルの上に当てても I のカーソルのままで、押せる雰囲気がありません。
アイコンやボタンの場合は、カーソルが指アイコンに変わって、押せる雰囲気になります。
ラベルの上に当てたときにカーソルを指にしたいです。
解決方法を2つみつけました。
1:透明なボタンをラベルの上に(前面に)配置する。
2:HTML テキストを配置して、<span style='cursor:pointer; color:blue'>編集</span>
のように style で cursor:pointer;
とする。
しかし...2つの方法では、それぞれデメリットがあります。
1:ホバーした時のラベルの色を変えられない。(ラベルより前面にボタンがあるため、ボタンのホバーになります。)
2:文字の範囲でしか反応しない。(カーソルが変わらない。)
2の方は、どういうことかというと、文字の周りの白いところでは反応しないということです。
これを解決する方法を見つけました!(どうしてもラベルでやりたいんだ!という方は、すみません。)
ボタンのプロパティをラベルっぽくすれば良いです。
Text="見た目がラベルのボタン"
BorderThickness=0
Color=Color.Black
DisabledBorderColor=RGBA(56, 56, 56, 1)
DisabledColor=RGBA(166, 166, 166, 1)
DisabledFill=Color.Transparent
Fill=Color.Transparent
FocusedBorderThickness=0
FontWeight=FontWeight.Normal
HoverBorderColor=Color.Transparent
HoverColor=Color.Red
HoverFill=Color.Transparent
PressedBorderColor=Color.Transparent
PressedFill=Color.Transparent
Size=13
ホバーの時の文字色を赤色にしています。
クリックできるラベルの文字列をコピペしたいという要件は無いとします。
HTML の知識がある場合、
<div style='cursor:pointer; 大きさとか余白を適当に指定'>
でやっても良いと思います。
非同期実行 Concurrent
Concurrent 関数で非同期実行(同時実行)ができます。
書式は、Concurrent( 式1, 式2 [, ...] )
で、
式1:凄く処理時間が長い
式2:式1の結果を待つ必要が無い
の場合、普通に式1;式2;
とすると、式2は、式1の実行結果とは無関係なのに、式1の実行結果を待ってから実行されます。
こういった状況の時は、Concurrent 関数で非同期実行(同時実行)で全体の実行時間を短くできます。
●Concurrent 未使用
ClearCollect( Product, '[SalesLT].[Product]' );
ClearCollect( Customer, '[SalesLT].[Customer]' );
ClearCollect( SalesOrderDetail, '[SalesLT].[SalesOrderDetail]' );
ClearCollect( SalesOrderHeader, '[SalesLT].[SalesOrderHeader]' )
●Concurrent 使用
Concurrent(
ClearCollect( Product, '[SalesLT].[Product]' ),
ClearCollect( Customer, '[SalesLT].[Customer]' ),
ClearCollect( SalesOrderDetail, '[SalesLT].[SalesOrderDetail]' ),
ClearCollect( SalesOrderHeader, '[SalesLT].[SalesOrderHeader]' )
)
参考:https://learn.microsoft.com/ja-jp/power-platform/power-fx/reference/function-concurrent
Select 関数も非同期です。
例えば、
Select(btnButton1);ClearCollect( Product, '[SalesLT].[Product]' )
と書いた場合、
Select(btnButton1)
とClearCollect( Product, '[SalesLT].[Product]' )
は、同時実行されます。参考:
https://github.com/MicrosoftDocs/powerapps-docs/issues/1208
https://qiita.com/bashaway/items/27e0bcc15f646d7151de
関数が作れる
プログラミング言語で当たり前のように存在する関数
function calculation(a, b) {
return a + b;
}
みたいなものを書きたい時、最初、Power Apps では、ノーコード/ローコードなだけにそういう概念が無いと思っていました。
後に Power Apps でも関数が定義できることに気付きました。
「実験段階」とされる設定をオンにします。仕様が変わっても対応が不可だったり、そもそもこの行為がNGだったりする場合は、使えません。
ここでは、引数一つで与えられた値が素数の場合、true
、それ以外の場合、false
を返す isPrimeNumber
という名の関数を作成していきます。
設定 → 近日公開の機能 → 実験段階 → 拡張コンポーネントのプロパティ をオンにします。
ツリービューから、コンポーネントを選択して、+新しいコンポーネント でコンポーネントを追加します。コンポーネント名を isPrimeNumber と変更しておきます。
右下の カスタムプロパティ → +新しいカスタム プロパティ をクリックして、isPrimeNumber
というプロパティを作成します。つまり、isPrimeNumber
という関数を作成します。ここで、
プロパティの型:出力
データ型:ブール値
とします。
+新しいパラメーター をクリックして、
パラメーター名:n
データ型:数値
必須:チェック有り
とします。これが関数の引数になります。
作成 をクリックします。
isPrimeNumber
プロパティを編集できるようになるため、引数 n
を使った式を書き込みます。
n
が素数の場合、true
、それ以外の場合、false
を返します。
If(
n = 2, true,//2の場合、素数
If(n < 2 Or Mod(n, 2) = 0,//2未満、2で割り切れたら素数じゃない。
false,
With(
{
tblNumbers:
AddColumns(
Sequence(n-2, 2, 1),
"divisible",
If(
//ここでのtrue,falseは、素数、素数以外の意味ではない。
Mod(n, Value) = 0,
true,//割り切れる
false//割り切れない
)
)
},
//割り切れるものが一つでもあれば、素数じゃない。
//breakしたいけど、やり方が思いつかず。
Not true in tblNumbers.divisible
)
)
)
ツリービューの画面編集に戻って、
+ → カスタム → isPrimeNumber にて先ほど作成したカスタムコンポーネントを挿入し、ラベル、テキスト入力を配置します。
テキスト入力の書式を 数値
にして、ラベルの Text には、以下のように記述しておきます。isPrimeNumber_1
は、カスタムコンポーネントの名前、isPrimeNumber
は関数の名前です。
If(isPrimeNumber_1.isPrimeNumber(TextInput1.Text),"素数だがね。","素数じゃにゃーわ。")
なお、値が大きすぎると、エラーになりました。
他、小数点が入力できたりしますが、このあたりの不都合なことは全く考慮に入れていません。
完成して気付きましたが、この関数いつ使うんだよってなりました。アプリで何回も必要な計算などに関数化する意味があると思います。
参考:https://mofumofupower.hatenablog.com/entry/custom_function_apps
ドロップダウン選択肢直指定
ドロップダウン選択肢の 項目 は、Choices(データソースの選択肢列)
でセットしたり、コレクションを設定したりできますが、Items に Value 列を持ったテーブルを直接書けば、データソース不要で選択肢が指定できます。["Seattle", "Tokyo", "London", "Johannesburg", "Rio de Janeiro"]
この書き方の場合、暗黙で Value 列を持っているテーブルになります。
複数列のテーブルを渡すと、デフォルトでは、アルファベット順の最初の列が採用されるようでした。[{c: 1, b: 10, a: "first"}, {c: 2, b: 20, a: "second"}]
Value のところを変更すると、採用される列を選択できます。
ちなみに、テーブルは、こうも書けます。(というか、この方が正しいかもしれないです。)Table({c: 1, b: 10, a: "first"}, {c: 2, b: 20, a: "second"})
レコード {c: 1, b: 10, a: "first"}
を渡してもOKで、その場合は、選択肢が1個だけになりました。
リストボックスにもギャラリーのデータソースにも Items にテーブルを直接書けます。
テーブル関数の説明にありました。
参考:https://learn.microsoft.com/ja-jp/power-platform/power-fx/reference/function-table
ギャラリーの中の部品をリセット
リセット= Default の値に戻す
という意味になります。
通常、Reset(部品)
でできますが、ギャラリーの中の部品は、Reset(部品)
でリセットできませんでした。
ギャラリーの中に TextInput1 があるとして、Reset(TextInput1)
のようにすると、以下のエラーになります。
Reset 関数はリセット可能なコントロールのみで使用できます。フォーム内のコントロールまたはギャラリー コントロールをリセットできるのは、同じフォームまたはギャラリー テンプレート内からのみです。
余談ですが、エラー文言をコピペできませんでした。他にもコピペしたくてもできない部分がちょいちょいあるような気がします。
Reset(Gallery1.AllItems)
Reset(Gallery1.AllItems.TextInput1)
でもダメです。
Reset 関数には、部品(コントロール)を指定しないとダメですが、この場合、テーブルを指定しているからです。
リセットしたい部品の Reset プロパティを true
にすると、リセットできました。
この Reset プロパティですが、何回もリセットする場合、true
にした後、false
に戻しておかないといけないです。
ギャラリー内のテキスト入力:
loc_TxtReset
リセットボタン:
UpdateContext({loc_TxtReset: true});
UpdateContext({loc_TxtReset: false});
Reset プロパティでリセットする方法は、ギャラリーの中の部品に限らず、有効です。
コレクション → フォームのフィールド
コレクションをフォームのデータソース(Items)に指定して、フォームでコレクションを操作しようとしても、フィールドの追加がグレーアウトされていて、コレクションのフィールドを追加できません。
この仕様に抗う裏技がありました。
一旦 コレクションと同一列を持つ Excel Online、Dataverse、Share Point のリストなどのデータソースを選んで、データカードを選抜後、フォームの Item を変更し、フォームのデータソースをコレクションに戻すと、コレクションと紐づけることができます。
「このフォームはまだデータに接続されていません データに接続してください」の表示が出ますが、問題無く使えます。
この続きで、例えば、ボタンにSubmitForm(Form1)
と記述すると、入力値の反映先がコレクションになります。
フォームの DefaultMode = FormMode.Edit
フォームの Item = 変更したいレコード
のとき、SubmitForm(Form1)
でレコード更新になります。
フォームの DefaultMode = FormMode.New
フォームの Item = 関係無し
のとき、SubmitForm(frm_IssueData)
でレコード追加になります。
Patch
ではなく、フォームを使う利点として、バリデーション(入力値の型チェック)が使える他、フォームのSubmitForm
で OnSuccess、OnFailure が使えます。
参考:http://powerappsguide.com/blog/post/set-form-data-source-to-a-collection
その他、宣伝、誹謗中傷等、当方が不適切と判断した書き込みは、理由の如何を問わず、投稿者に断りなく削除します。
書き込み内容について、一切の責任を負いません。
このコメント機能は、予告無く廃止する可能性があります。ご了承ください。
コメントの削除をご依頼の場合はTwitterのDM等でご連絡ください。