こんにちは、あんこ先生です。
ここまでで、投稿が並ぶタイムラインと、いいね機能ができました。
でも、肝心の「ありがとう」を送る画面がまだありませんでしたよね。
今回はそこを作ります。
送信先を選んで、メッセージを書いて、タグを付けて、画像も添えて。
これができると、ようやくアプリの中だけで一周できるようになりますよ。
まだ読んでいない人は、先にいいね機能とコレクションの回まで進めておいてくださいね。
画面の骨組み

新しい画面を追加して、名前を scrPost にします。
第2回のボトムメニューで、行き先として名前だけ作っておいた画面ですね。
上から順に、送信先・メッセージ・タグ・画像を縦に並べて、いちばん下に投稿ボタンを置きます。
入力欄をまとめるコンテナ conPostForm を1つ置いて、その中に部品を入れていきましょう。
| プロパティ | 設定する式 |
|---|---|
| Fill | RGBA(255, 255, 255, 1) |
| Y | 70 |
| Height | Parent.Height – 70 – 64 |
| Width | Parent.Width |
画面の OnVisible には、入力状態のリセットを入れておきます。
//画面 scrPost の OnVisible(入力状態を初期化する)
//二重送信を防ぐフラグを倒して、前回選んだタグを消す
UpdateContext({ ctxIsSubmitting: false });
Clear(colSelectedTags)この colSelectedTags は、あとでタグを選ぶときに使うコレクションです。
前の投稿のタグが残っていると気持ち悪いので、画面を開くたびに空にしています。
部品は縦に積んでいく
この画面は「見出しラベル+入力欄」を4組、縦に並べる形になります。
ここで、第2回のタイムラインでやった「ひとつ上の下端から積む」を、そのまま使います。
//入力欄の Y(ひとつ上の見出しラベルの下端から積む) lblToUser.Y + lblToUser.Height
Y座標を数字で決め打ちしないのは、今回も同じ理由です。
あとから見出しの文字を大きくしたり、入力欄の高さを変えたりしても、下の部品が勝手についてきてくれます。
とくにタグの部分は、タグの数によって高さが変わります。
決め打ちしていると、タグを1つ足しただけで画像欄が隠れる、なんてことになりますからね。
送信先を選ぶ
まずは「誰にありがとうを送るか」の部分から。
見出しのテキストラベル lblToUser を置いて、「送信先」と入れておきます。
| プロパティ | 設定する式 |
|---|---|
| Text | “送信先” |
| FontWeight | FontWeight.Semibold |
| Size | 16 |
| Height | 32 |
| Width | Parent.Width – 40 |
| X | 20 |
| Y | 16 |
その下に、モダンドロップダウンを置いて名前を drpToUser にします。
| プロパティ | 設定する式 |
|---|---|
| Items | glbMembers |
| ItemDisplayText | ThisItem.DisplayName |
| Width | Parent.Width – 40 |
| X | 20 |
| Y | lblToUser.Y + lblToUser.Height |
glbMembers は、第1回の App.OnStart で作った部署メンバーの一覧です。
ここでいきなり、ふたつの落とし穴があります。
グループからメンバーを取ってくる方法は、全社員を把握!Office365ユーザー&グループコネクタの使い方にまとめてあります。
メッセージを書く
次はメッセージの入力欄です。
同じように、見出しラベル lblBodyLabel(「メッセージ」)を置きます。
Yは、ひとつ上のドロップダウンの下端から。
| プロパティ | 設定する式 |
|---|---|
| Text | “メッセージ” |
| Height | 32 |
| Y | drpToUser.Y + drpToUser.Height + 5 |
その下にモダンテキスト入力を置いて、名前を txtBody に。
| プロパティ | 設定する式 |
|---|---|
| Type | TextInputType.Multiline |
| Placeholder | “感謝の気持ちを書きましょう” |
| Height | 120 |
| Width | Parent.Width – 40 |
| X | 20 |
| Y | lblBodyLabel.Y + lblBodyLabel.Height |
複数行にしたいので Type を Multiline にしています。
表にすると、こんな対応になります。
| やりたいこと | つい書いてしまう | 正しい書き方 |
|---|---|---|
| 複数行にする | Mode | Type |
| 薄い文字を出す | HintText | Placeholder |
| 入力された値を読む | .Value | .Text |
タグを選べるようにする
さて、ここがこの画面でいちばん楽しいところです。
タグは「#助かりました」と「#ナイス頑張」のように、複数まとめて付けられるようにしたいですよね。
コンボボックスでも選べるのですが、スマホで使うことを考えると、指でポンポン押せるほうが気持ちいいです。
そこで、タグをチップ(丸いボタン)にして、ギャラリーで並べます。
チップを並べる
まず見出しラベル lblTagsLabel(「タグ(複数選択可)」)を置きます。
| プロパティ | 設定する式 |
|---|---|
| Text | “タグ(複数選択可)” |
| Height | 32 |
| Y | txtBody.Y + txtBody.Height + 5 |
その下にギャラリーを置いて、名前を galTags にします。
レイアウトは「縦」を選んでください。
横に並べたいのに縦?と思いますよね。
縦ギャラリーには WrapCount という「1行に何個並べるか」の設定があって、これを使うと格子状に並べられます。
行の高さが TemplateSize で決まるので、全体の高さを計算しやすいのがうれしいところです。
| プロパティ | 設定する式 |
|---|---|
| Items | Choices(Posts.Tags) |
| WrapCount | 3 |
| TemplateSize | 44 |
| Height | RoundUp(CountRows(Choices(Posts.Tags)) / 3, 0) * 44 |
| TemplatePadding | 0 |
| Width | Parent.Width – 40 |
| X | 20 |
| Y | lblTagsLabel.Y + lblTagsLabel.Height |
Choices を使うと、SharePointの選択肢列に登録した文言をそのまま持ってこられます。
タグを増やしたくなったらSharePoint側に足すだけでいいので、アプリを直さずに済みますね。
タグが増えても崩れないようにする
Height の式が、この画面でいちばん大事なところかもしれません。
//タグギャラリー galTags の Height(タグの数から必要な段数を出して高さを決める) //1行に3個並べるので、3で割って切り上げた数が段数になる RoundUp(CountRows(Choices(Posts.Tags)) / 3, 0) * 44
タグの数を1行あたりの数(3)で割って、RoundUp で切り上げると、必要な段数が出ます。
それに1段の高さ(44)を掛ければ、ちょうど収まる高さになるわけですね。
タグが5個なら2段で88、7個なら3段で132、という具合に自動で伸びます。
//NG:これは「タグの数」ではない CountRows(Posts.Tags) //OK:選択肢の数を数える CountRows(Choices(Posts.Tags))
Posts のうしろに列名を付けた Posts.Tags は、全部の投稿のTags列を縦に並べた表になります。つまり数えているのは投稿の件数であって、タグの選択肢の数ではないんですね。これに気づかないと、投稿が増えるたびにタグの並びが変わるという、なんとも不思議な動きになります。選択肢そのものを数えたいときは、Choices でくるむのを忘れずに。
タップで選択・解除できるようにする
ギャラリーの中にモダンボタンを1つ置いて、名前を btnTag に。
| プロパティ | 設定する式 |
|---|---|
| Text | ThisItem.Value |
| Appearance | If(ThisItem.Value in colSelectedTags.Value, ButtonAppearance.Primary, ButtonAppearance.Transparent) |
| BasePaletteColor | RGBA(0, 120, 212, 1) |
| Height | 36 |
| Width | Parent.TemplateWidth – 8 |
| RadiusTopLeft ほか4隅 | 18 |
角丸を18にしているので、丸っこいチップの見た目になります。
そして OnSelect に、選択と解除を切り替える式を入れます。
//タグボタン btnTag の OnSelect(押すたびに、選択したり外したりする)
//すでに選んでいれば外す、まだなら足す
If(
ThisItem.Value in colSelectedTags.Value,
RemoveIf(colSelectedTags, Value = ThisItem.Value),
Collect(colSelectedTags, { Value: ThisItem.Value })
)前回のいいね機能を作った人なら、この形に見覚えがあるはずです。
そう、まったく同じトグルの書き方ですね。
画像を添える
画像は「画像の追加」コントロールを使います。
まず見出しラベル lblImageLabel(「画像(任意・1枚)」)を、タグギャラリーの下に置きます。
| プロパティ | 設定する式 |
|---|---|
| Text | “画像(任意・1枚)” |
| Height | 32 |
| Y | galTags.Y + galTags.Height + 5 |
タグが増えて galTags が伸びても、この見出しがちゃんと下がってくれます。
その下に「画像の追加」コントロール medAddImage と、プレビュー用の画像コントロール imgPreview を置きます。
| コントロール | プロパティ | 設定する式 |
|---|---|---|
| medAddImage | Height | 48 |
| medAddImage | Y | lblImageLabel.Y + lblImageLabel.Height |
| imgPreview | Image | medAddImage.Media |
| imgPreview | Visible | !IsBlank(medAddImage.Media) |
| imgPreview | Width | If(IsBlank(medAddImage.Media), 0, 300) |
| imgPreview | Height | Self.Width |
| imgPreview | Y | medAddImage.Y + medAddImage.Height + 5 |
選んだ画像がその場で見えるので、間違えたときに気づけます。
じつはこのプレビュー、見た目のためだけではありません。
//NG:選んだ画像をそのまま保存しようとする Image: medAddImage.Media //OK:プレビュー用の画像コントロール経由で保存する Image: imgPreview.Image
つまり imgPreview は、プレビューであると同時に保存のための中継役でもあるわけですね。「なぜかエラーになる」で止まりやすいところなので、覚えておくと助かります。
投稿ボタンを作る
いよいよ保存です。
モダンボタンを画面の下に置いて、名前を btnSubmit にします。
| プロパティ | 設定する式 |
|---|---|
| Text | If(ctxIsSubmitting, “送信中…”, “投稿する”) |
| DisplayMode | If(ctxIsSubmitting || IsBlank(drpToUser.Selected) || IsBlank(txtBody.Text), DisplayMode.Disabled, DisplayMode.Edit) |
| Height | 44 |
| Width | Parent.Width – 40 |
| X | 20 |
| Y | Parent.Height – 64 – 44 – 12 |
DisplayMode で、送信先とメッセージが空のあいだはボタンを押せないようにしています。
送信中も押せなくしているので、二重送信の防止にもなりますね。
保存の中身
OnSelect に入れる式です。
//投稿ボタン btnSubmit の OnSelect(入力内容をPostsリストに保存する)
//送信中フラグを立ててから登録し、終わったらタイムラインへ戻る
UpdateContext({ ctxIsSubmitting: true });
Patch(
Posts,
Defaults(Posts),
{
PostID: GUID(),
Body: txtBody.Text,
ToUser: drpToUser.Selected,
Tags: colSelectedTags,
Image: imgPreview.Image,
LikeCount: 0,
CommentCount: 0
}
);
Notify("ありがとうを送りました", NotificationType.Success);
Navigate(scrTimeline, ScreenTransition.None)PostID には GUID で作った文字列を入れています。
いいねやコメントが、この投稿を見つけるための目印ですね。
Tags には、さっき作った colSelectedTags をそのまま渡しています。
列名を Value でそろえておいたおかげで、詰め替えなしで入りました。
LikeCount と CommentCount は、0 で始めておきます。
Patch の基本は、Patch関数の使い方 基本編もあわせてどうぞ。
ユーザー列に、あっさり入る理由
ここで ToUser の行を見てください。
//送信先。ドロップダウンで選んだ相手を、そのまま渡すだけ ToUser: drpToUser.Selected
たったこれだけです。
SharePointのユーザー列は書き込みにクセがあることで有名なのですが、ここではあっさり通ります。
じつはこれ、第1回の下ごしらえが効いているんですね。
SharePointのユーザー列は、決まった顔ぶれのキーがそろったかたまりでないと受け取ってくれません。
‘@odata.type’ ・ Claims ・ DisplayName ・ Email ・ JobTitle ・ Picture ・ Department の7つです。
そして第1回で作った glbMembers を思い出してください。
//第1回のおさらい:ユーザー列と同じ形にそろえてある
//前半で @odata.type / Claims / DisplayName / Email / JobTitle
//後半の AddColumns で Picture / Department → あわせて7つ
Set(glbTemp, ForAll(..., { '@odata.type': ..., Claims: ..., DisplayName: ..., Email: ..., JobTitle: ... }));
Set(glbMembers, AddColumns(glbTemp, Picture, ..., Department, Blank()))ちょうど7つ、そろっていますよね。
第1回で「ユーザー列が受け取れる形に翻訳している」と書いたのは、まさにこのことでした。
出どころのデータを最初から正しい形で作っておいたので、選んだレコードをそのまま流し込むだけで入るというわけです。
ユーザー列への書き込みは方法がいくつかあるので、様々なアプローチでユーザー列に書き込む方法もどうぞ。
相手にメールで知らせる
最後に、ひと手間だけ足しましょう。
せっかくありがとうを送っても、相手がアプリを開かないと気づけませんよね。
そこで、投稿と同時にメールを飛ばします。
さきほどの Patch の直後、Notify の前に、こちらを差し込んでください。
//投稿ボタン btnSubmit の OnSelect(保存のあとに追記する)
//送信先へありがとうメールを飛ばす。タグを選んでいれば本文に添える
Office365Outlook.SendEmailV2(
drpToUser.Selected.Email,
User().FullName & "さんからありがとうが届きました",
"<p><b>" & User().FullName & "</b> さんからありがとうが届きました。</p>"
& "<p>" & txtBody.Text & "</p>"
& If(CountRows(colSelectedTags) > 0,
"<p>#" & Concat(colSelectedTags, Value, " #") & "</p>",
""
)
);本文はHTMLで書けるので、名前を太字にしたり、タグを並べたりできます。
Concat でタグをつなげているところは、タイムラインでタグを表示したときと同じ書き方ですね。
なお、この機能を使うには Office365Outlook コネクタをアプリに追加しておく必要があります。
データの追加から「Office 365 Outlook」を選んでおいてください。
まとめ
今回はここまでにしましょう。
・ドロップダウンの表示列は ItemDisplayText。名前が出ないときは、まずOnStartを実行してみる。
・モダンのテキスト入力は Type ・ Placeholder ・ .Text。クラシックとは名前が違う。
・タグの複数選択は、チップのギャラリーで自作すると気持ちいい。列名を Value でそろえておけば、そのまま保存できる。
・入力欄のYは、第2回と同じく「ひとつ上の下端から」積む。タグの数で高さが変わるので、決め打ちすると崩れる。
・タグの段数は RoundUp で計算して、ギャラリーの高さに反映させる。数えるのは Choices の中身。
・画像は、プレビュー用のコントロール経由で保存する。
・ユーザー列は、出どころのデータを同じ形で作っておけばそのまま渡せる。自分で組むときは、値は Claims だけでいいがキーは7つ全部そろえる。
これでアプリの中だけで、投稿してタイムラインで見て、いいねを押すところまで一周できるようになりました。
次の一手として、次回はコメント機能を作ります。
投稿をタップすると詳細画面が開いて、そこにコメントが並ぶ形ですね。
いいねで使った「関連するリストを投稿IDで探す」考え方が、そのまま活きてきますよ。
それでは、次の記事でお会いしましょう。
















・メッセージの入力欄
・タップで選べるタグ(複数選択)
・画像の添付(1枚)
・投稿ボタンと、相手へのメール通知