はじめに
7/31(土)にこちらの勉強会で登壇させていただきました。
前々からやりたいって言っていたイベントを開催できたので私は満足です。
登壇を引き受けてくださった、Akira さん、Hiro さん、司会をしてくださったやまさん、ありがとうございました!!!!
この勉強会の内容はAkira さんがこちらのブログで纏めてくれていますので、参加されてない方はこちらで他の方の登壇内容もチェックしてみてください。
また、Connpass のページにて資料も公開されていますので、興味を持たれた方は是非ご一読ください。
私の登壇資料はこちらです。
また、当日の私の登壇の様子は私のYouTube のchannel にアップしていますので、合わせて確認していただけますと幸いです。
もしよろしければ、チェンネル登録、高評価、Twitter のフォローなどよろしくお願いします!(テンプレート)
さて、前置きはここまでにして、私はこの勉強会のなかでコンポーネントでレコードやテーブルを扱う際には作成時に宣言したフィールドとは異なるフィールドが設定されたれこーどやテーブルは渡すことができない。と説明しました。
こちらの内容に間違いはないのですが、"情報"であれば渡すことが可能です。
この記事ではその方法を紹介したいと思います。
ただはじめに断っておくと、これはあくまで"こうすればできないこともないよ"という内容ですので、実際利用する際は
ということを考慮したうえで、利用を行ってください。
コンポーネントにコレクションの情報を渡す
ロジック
勉強会でも説明した通り、コンポーネント作成時に宣言したフィールドと異なるフィールドが設定されたレコード/テーブルをコンポーネントに渡すことはできません。
ではどうするかというと、コレクションをJSON 化してコンポーネントに渡します。
JSON 形式に変換されたコレクションはテキスト型として扱うことが可能ですので、コンポーネント側はテキストデータを受け取ることになります。
このやり方はPower Apps からPower Automate にコレクション情報を渡した経験がある方であれば馴染の深いやりかたかもしれません。
Power Apps で実装する
まずコンポーネントには、JSON フォーマットに変換されたコレクション情報を受け取るためのプロパティ or パラメータを用意します。
今回は入力プロパティを用意します。
このプロパティには、以下のようにして変換した値を渡す必要があります。
Set( JsonCol, JSON( Table( {SampleStringField: "SampleText1", SampleNumberField: 10, SampleBooleanField: true}, {SampleStringField: "SampleText2", SampleNumberField: 100, SampleBooleanField: false} ), JSONFormat.IndentFour ) )
上の式では、Table 関数で定義されたテーブル情報をJSON 関数でJSON 化しています。
変換されたテキスト値が JsonCol
に設定されるので、コンポーネントには JsonCol
を渡してあげればよいです。
これで、コンポーネントにコレクション情報を渡すことができるようにはなったわけですが、残念なことにPower Apps でJSON データを解析するような関数は用意されておりません。
なので、自力でJSON データを解析して、key とvalue を得る必要があります。
項目の名前を抜き出す
先ほどの式を実行すると、 JsonCol
には以下のようなJSON データが設定されます。
JsonCol
[ { "SampleBooleanField": true, "SampleNumberField": 10, "SampleStringField": "SampleText1" }, { "SampleBooleanField": false, "SampleNumberField": 100, "SampleStringField": "SampleText2" } ]
今は項目の名前を取り出したいので、上記でいうと SampleStringField
, SampleNumberField
, SampleBooleanField
になります。
これらを取り出すためには、正規表現を用いたいと思います。
正規表現を用いてテキストの抽出を多なうので、MatchAll 関数を利用します。
項目の名前は "
と ":
で囲まれた値ですので、これを正規表現で表したいと思います。
上記を踏まえて式を記載すると以下のようになります。
MatchAll(Self.JSONFormatCollection, "(?<="").*?(?="":)")
第一引数が、JSON データで第二引数が項目の値を取り出すための正規表現となっています。
正規表現???という方も多いと思いますので、簡単に解説します。
(?<=[パターン])
これで"肯定後読み"と呼ばれる表現方法になります。
肯定後読みでは、[パターン]にマッチした場合にその部分の文字列の末尾位置にマッチすることになります。
今回 ""
としているので "
の次の文字の位置にマッチすることになります。
.*?
これは"最短マッチ"と呼ばれる表現方法です。
この表現の直前のパターンに対応する直後に記載されたパターンまでを最短でマッチさせるための表現になります。
これは実際の動きを試してもらうと早いと思います。
下記サイトで <.*>
と <.*?>
の2パターンの正規表現で <ABC><DEF>
をチェックしてみてください。
(?<=[パターン])
これで"肯定先読み"と呼ばれる表現方法になります。
肯定後読みとは異なり、[パターン]にマッチした場合にその部分の文字列の先頭位置にマッチすることになります。
今回 "":
としているので、 ":
の前の文字の位置にマッチすることになります。
Power Apps の式に戻ります。
この記載方法ですと、複数レコード存在するとレコード数分項目名がとられてしまいます。
項目名は重複していないはずであるので、Distinct 関数で重複を削除してあげましょう。
Distinct(MatchAll(Self.JSONFormatCollection, "(?<="").*?(?="":)"), FullMatch)
これにより、JSON データからkey 項目、コレクションの項目名を取得することができました。
項目の値を取り出す
最後に項目の値を取り出そうと思います。
項目の値は ":
と \r\n|\n
(改行を表しています。)で囲まれている値を取得すれば、取得できそうです。
ただし、テキストデータは "
で囲まれており、データは ,
で区切られているのでこれらを除去する必要があります。
これらを踏まえて式に表すと、以下のようになります。
With( {colVal: Distinct(MatchAll(Parent.JSONFormatCollection, "(?<="": ).*?(?=(\r\n|\n))"), FullMatch)}, ForAll( Sequence(CountRows(colVal)), { Id: ThisRecord.Value, Value: Substitute(Substitute(Last(FirstN(colVal, ThisRecord.Value)).Result, """", ""), ",", "") } ) )
"
と ',' の除去にはSubstitute 関数を利用しています。
これにより項目の値を抜き出すこと自体は成功するのですが、値がただ列挙されたテーブルを取得できるだけで、項目の名前との紐づけが行えません。
これらを紐づける解決策の1つとして、名前と値をそれぞれ紐づけるためのID をもたせてあげればよいです。
名前にID を順に振り、値はID 値を名前の数で割ったあまりで紐づければいいというわけです。
ここの詳しい実装方法と解説は以下コンポーネントの解説で行おうと思いますので、少しお待ちください。
#PowerApps でユーザが任意のフィールドを選択して表示できるようなデータテーブルのコンポーネント作ってみた
— コルネ (@koruneko32767) July 19, 2021
細かいとこはまだだけど、とりあえず動くものできた pic.twitter.com/Vn0ptoiMx6
この記事では式だけ記載しておきます。
わかる人はここから読み取ってください(という無茶ぶりです)。
項目の名前にID を割り振る。
ForAll( Sequence(CountRows(Parent.ColumnNames)), { Id: ThisRecord.Value, Result: Last(FirstN(Parent.ColumnNames, ThisRecord.Value)).Result } )
選択した項目の名前と、項目の値を紐づける。
With( { colAllVal: With( {colVal: Distinct(MatchAll(Parent.JSONFormatCollection, "(?<="": ).*?(?=(\r\n|\n))"), FullMatch)}, ForAll( Sequence(CountRows(colVal)), { Id: ThisRecord.Value, Value: Substitute(Substitute(Last(FirstN(colVal, ThisRecord.Value)).Result, """", ""), ",", "") } ) ) }, With( {ColumnNameGalleryAllItems: CustomDataTableComponent_ColumnNameSelectGallery.AllItems}, ForAll( Sequence(CountIf(ColumnNameGalleryAllItems, CustomDataTableComponent_ColumnNameCheckBox.Value = true)) As CheckCount, With( {checkItem: Last(FirstN(Filter(ColumnNameGalleryAllItems, CustomDataTableComponent_ColumnNameCheckBox.Value = true), CheckCount.Value))}, { key: checkItem.Result, val: Filter(colAllVal, Mod(Id, CountRows(Parent.ColumnNames)) = Mod(checkItem.Id, CountRows(Parent.ColumnNames))) } ) ) ) )
これで入れ子になったテーブルが取得できるので、ギャラリーの中にギャラリーやリストを設定してあげればよいです。
おわりに
最後説明が放り投げた状態で終わらせてしまいましたが、ちょっとここの説明までいれてしまうと長くなってしまいそうですので、次回に回させてください。
記事の途中で説明した正規表現はPower Apps 以外でも覚えておくと大変便利なパターンマッチ方法ですので、気になった方は是非この機会に勉強してみてください。