PowerApps│感謝を送り合う社内SNSアプリの作り方 ⑤コメント機能

こんにちは、あんこ先生です。

前回で投稿画面ができて、アプリの中だけで一周できるようになりました。

今回は最後のSNSらしい機能、コメントを付けていきます。

投稿をタップすると詳細画面が開いて、そこにコメントが並ぶ形ですね。

いいねのときに使った「関連するリストを投稿IDで探す」考え方が、そのまま活きてきますよ。

この記事で作るもの
・タイムラインのコメントボタン(押すと詳細画面へ)
・投稿の詳細画面と、コメントの一覧
・コメントの入力欄と送信
・投稿ごとのコメント数の更新

まだ読んでいない人は、先に投稿画面の回まで進めておいてくださいね。

まずは開くボタンから

タイムラインのいいねボタンの右に、コメントボタンを置きましょう。

モダンボタンを追加して、名前を btnComment にします。

プロパティ設定する式
Icon“Chat”
TextText(ThisItem.CommentCount)
LayoutButtonLayout.IconBefore
AppearanceButtonAppearance.Transparent
ColorRGBA(120, 120, 120, 1)
Height32
Width90
X186
YbtnLike.Y

いいねボタンと同じ高さに並べたいので、YbtnLike.Y をそのまま参照しています。

こう書いておけば、上の部品が伸びていいねボタンが下がったときも、コメントボタンが置いていかれません。

どの投稿を開いたか、を渡す

押したときの処理です。

//コメントボタン btnComment の OnSelect(詳細画面をひらく)
//どの投稿を開いたのかを変数に入れてから移動する
Set(glbSelectedPost, ThisItem);
Navigate(scrPostDetail, ScreenTransition.None)

画面を移動すると、当然ながら「ギャラリーの何行目を押したか」という情報は失われます。

そこで、押された行そのものを glbSelectedPost に入れてから移動しています。

移動先では、この変数を見れば投稿の中身が分かる、というわけですね。

グローバル変数の使いどころは、グローバル変数でコントロールを変化させる方法にもまとめてあります。

詳細画面を作る

新しい画面を追加して、名前を scrPostDetail にします。

上から順に、戻るボタン、投稿の中身、コメント一覧、入力欄という並びです。

開いたときにコメントを読み込む

画面の OnVisible に、こちらを入れます。

//画面 scrPostDetail の OnVisible(この投稿へのコメントを新しい順に読み込む)
//送信中フラグを倒してから、PostID で絞って取得する
UpdateContext({ ctxCommentSending: false });

ClearCollect(
    colComments,
    SortByColumns(
        Filter(Comments, PostID = glbSelectedPost.PostID),
        "Created",
        SortOrder.Descending
    )
)

Comments リストを PostID で絞って、新しい順に並べています。

いいねのときに Likes を絞ったのと、まったく同じ考え方ですね。

投稿とコメントは PostIDという合言葉でつながっている、とイメージすると分かりやすいと思います。

投稿の中身を再表示する

画面の上のほうに、いま開いている投稿を表示します。

コンテナ conPostBody を置いて、その中にアバターと本文を並べましょう。

参照するのは、さっき渡した glbSelectedPost です。

コントロールプロパティ設定する式
avaPostImageglbSelectedPost.登録者.Picture
lblDetailAuthorTextglbSelectedPost.Author.DisplayName & ” → ” & glbSelectedPost.ToUser.DisplayName
lblDetailBodyTextglbSelectedPost.Body
lblDetailTagsTextConcat(glbSelectedPost.Tags, Value & ” “)

タイムラインで書いた式の ThisItem を、glbSelectedPost に置き換えただけです。

アバターの画像だけ 登録者 と日本語で書くところも、タイムラインと同じですね。

コメントを並べる

その下に、コメントの一覧を置きます。

ギャラリーを追加して、名前を galComments に。

レイアウトは、タイムラインと同じ「縦(可変の高さ)」を選んでください。

コメントも長さがバラバラですから、行の高さが伸び縮みしてくれないと困りますよね。

プロパティ設定する式
ItemscolComments
WidthParent.Width
TemplatePadding0

中に置く部品は、タイムラインの縮小版です。

コントロールプロパティ設定する式
avaCommentImageThisItem.登録者.Picture
avaCommentWidth36
lblCommentAuthorTextコメント者名と相対時刻(下で解説)
lblCommentAuthorAutoHeighttrue
lblCommentBodyTextThisItem.CommentBody
lblCommentBodyAutoHeighttrue
lblCommentBodyYlblCommentAuthor.Y + lblCommentAuthor.Height + 2

ここでも AutoHeight とY座標の連鎖を使っています。

第2回で覚えたやり方が、そのまま別の画面でも通用するわけですね。

コメント者と時刻の表示も、タイムラインで作った式を流用できます。

//ラベル lblCommentAuthor の Text(コメントした人と、経過時間)
//タイムラインで作った相対時刻の式を、そのまま持ってきている
ThisItem.Author.DisplayName
& "  ・ "
& With(
    { mins: DateDiff(ThisItem.Created, Now(), TimeUnit.Minutes) },
    Switch(
        true,
        mins < 1,     "たった今",
        mins < 60,    Text(mins) & "分前",
        mins < 1440,  Text(RoundDown(mins / 60, 0)) & "時間前",
        mins < 10080, Text(RoundDown(mins / 1440, 0)) & "日前",
        Text(ThisItem.Created, "yyyy/mm/dd")
    )
)

同じ式を2か所に書くのが気になる人は、名前付き数式にまとめる手もありますよ。

コメントを送る

画面のいちばん下に、入力欄と送信ボタンを置きます。

コントロールプロパティ設定する式
txtCommentPlaceholder“コメントを入力”
txtCommentHeight44
txtCommentWidthParent.Width – 40 – 56
txtCommentYParent.Height – 64 – 44 – 10
btnSendCommentIcon“Send”
btnSendCommentLayoutButtonLayout.IconOnly
btnSendCommentDisplayModeIf(ctxCommentSending || IsBlank(txtComment.Text), DisplayMode.Disabled, DisplayMode.Edit)
btnSendCommentXParent.Width – 56

入力が空のあいだは送信ボタンを押せないようにしています。

送信の中身

OnSelect に入れる式です。

//送信ボタン btnSendComment の OnSelect(コメントを登録して、その場で一覧に反映する)
//1. Commentsに登録 2. 投稿のコメント数を1増やす 3. 一覧を取り直す 4. 入力欄を空にする
UpdateContext({ ctxCommentSending: true });

Patch(
    Comments,
    Defaults(Comments),
    {
        PostID: glbSelectedPost.PostID,
        CommentID: GUID(),
        CommentBody: txtComment.Text
    }
);

With(
    { cur: LookUp(Posts, PostID = glbSelectedPost.PostID) },
    Patch(Posts, cur, { CommentCount: cur.CommentCount + 1 })
);

ClearCollect(
    colComments,
    SortByColumns(
        Filter(Comments, PostID = glbSelectedPost.PostID),
        "Created",
        SortOrder.Descending
    )
);

Reset(txtComment);
UpdateContext({ ctxCommentSending: false })

コメント本体の登録は、PostIDCommentIDCommentBody の3つだけ。

コメントした人と日時は、いつものように 登録者登録日時 が自動で入ります。

そのあと ClearCollect で一覧を取り直しているので、送信した瞬間に自分のコメントが並びます。

最後の Reset は、入力欄を空に戻すためのものですね。

ここでハマりました

さて、この記事でいちばんお伝えしたいのが、次のところです。

わたしは最初、コメント数を増やす部分をこう書いていました。

//一見よさそうだけど、うまくいかない書き方
Patch(
    Posts,
    LookUp(Posts, PostID = glbSelectedPost.PostID),
    { CommentCount: glbSelectedPost.CommentCount + 1 }
)

開いている投稿のコメント数に1を足す。素直な書き方に見えますよね。

ところが実際に試すと、何回コメントしても、コメント数が1のままだったんです。

図解を貼り付ける:変数は画面を開いた時点の写しなので、glbSelectedPost.CommentCount + 1 は何回やっても同じ答えになる。その都度LookUpで読み直せば1、2、3と増える、という比較図。zukai_variable_snapshot.png を使用。

原因は glbSelectedPost の性格でした。

この変数は、画面を開いた瞬間にコピーした写しなんですね。

コメントを登録してSharePoint側の数が増えても、変数の中身は開いたときのまま。ずっと0です。

だから「0 + 1」を何度も計算していた、というわけでした。

その都度、読み直す

直し方は、いまの値を毎回取りに行くことです。

//投稿のコメント数を1増やす(押すたびに、いまの値を読み直してから足す)
//With でいったん受け取っておくと、同じ問い合わせを2回書かずに済む
With(
    { cur: LookUp(Posts, PostID = glbSelectedPost.PostID) },
    Patch(Posts, cur, { CommentCount: cur.CommentCount + 1 })
)

LookUp で投稿を探し直してから、そのコメント数に1を足しています。

With を使って cur という名前で受け取っているのは、同じ LookUp を2回書かないためです。

同じ値を何度も使うときに便利な書き方なので、With関数でコードを読みやすく省力化する方法もどうぞ。

ここが個人的に一番おもしろい:いいねはなぜ大丈夫だったのか
思い出してほしいのですが、いいねの数はちゃんと増えていましたよね。
あちらはこう書いていました。

Patch(Posts, ThisItem, { LikeCount: ThisItem.LikeCount + 1 })

ポイントは ThisItem です。

これはギャラリーの「その行そのもの」を指していて、写しではありません。だから押した瞬間の最新の値が入っていて、何度押しても正しく増減してくれたわけです。同じ「1を足す」でも、足す相手が写しなのか本物なのかで結果が変わる。わたしはここで小一時間悩みました。同じ罠にハマる人が減るとうれしいです。

LookUp の使い方は、LookUp関数の正しい使い方にくわしくまとめてあります。

戻るボタンを付けておく

最後に、タイムラインへ帰れるようにしておきましょう。

画面の左上にモダンボタンを置いて、名前を btnBack にします。

プロパティ設定する式
Icon“Back”
LayoutButtonLayout.IconOnly
AppearanceButtonAppearance.Transparent
OnSelectNavigate(scrTimeline, ScreenTransition.None)

スマホだと端末の戻る操作もありますが、画面の中にも置いておくと親切ですね。

スクショを貼り付ける:投稿の詳細画面。上に投稿の中身、その下にコメントが新しい順に並び、いちばん下に入力欄と送信ボタンがある状態。

まとめ

今回はここまでにしましょう。

・別の画面へ「どれを開いたか」を渡すときは、押された行を変数に入れてから移動する。
・コメントは PostID で絞って取ってくる。いいねのときとまったく同じ考え方。
・数を増やすときは、足す相手が写しなのか本物なのかを確かめる。変数は開いたときのまま止まっている。
・登録したあとに ClearCollect で取り直すと、その場で一覧に反映される。

これで投稿・タイムライン・いいね・コメントと、SNSらしい部分がひととおり揃いました。

次の一手として、次回はマイページを作ります。

自分が送ったありがとうの数、受け取った数、最近もらったメッセージを並べる画面ですね。

第1回で「カウント専用のリストは作らない」と決めた理由が、ここで分かるはずですよ。

それでは、次の記事でお会いしましょう。

ABOUT US
七草あんこ
非IT系中間管理職やってます。社命によりoffice365を主軸とした業務改善プロジェクトメンバーに任命されたことでPowerAppsと出会えました。いまではビジネス・プライベートを問わず、欠かせないツールになっています。導入初期やアプリ作成時に遭遇した諸問題の解決法とサンプルアプリの作り方を紹介していきます。主にTwitterで情報収集しているので不明点など呟いているとお邪魔するかもしれません。