Power Apps で下書き保存を実装する|複数画面・status列・条件付きPatchの設計パターン

複数画面にまたがる入力フォームで「途中まで入力して後で続ける」を実現したいなら、下書き保存の仕組みが必要です。難しそうに見えますが、status列・varRecord・条件付きPatchの3つを押さえれば実装できます。

下書き保存が必要なシーン

下書き保存が必要になるのは、現場での点検入力で途中に呼び出しが入るケース、複数画面にまたがる申請フォームで一度に全部入力できないケース、承認フローに回す前に内容を確認しながら段階的に埋めたいケースなどです。

いずれも共通しているのは「入力途中の状態を安全に保持したい」という要件です。フォームを閉じてもデータが消えず、次回開いたときに続きから始められることが求められます。アプリの変数だけで管理しようとすると、アプリを閉じた瞬間に状態が消えてしまいます。これを防ぐためにデータソース側にstatus列を持たせます。

status列をテーブルに追加する

下書き保存の核心はstatus列です。SharePointリストやDataverseテーブルに「draft / open / in progress / closed」のような選択肢を持つ列を追加します。アプリのユーザーはこの列を直接触る必要はありません。あくまでアプリ側が制御するための内部管理列です。

なぜこの列が必要かというと、「このレコードがまだ提出されていない」という状態をデータ側で識別するためです。列があることで「draft状態のレコードだけギャラリーに編集アイコンを出す」「draftでないレコードはすべて入力不可にする」といった分岐ができます。

新規ボタンでdraftレコードを即座に作成する

新規ボタンが押されたタイミングで、空のdraftレコードをその場で作成します。そのレコードをvarRecordに格納することが大切です。

Set(
    varRecord,
    Patch(
        IncidentManagement,
        Defaults(IncidentManagement),
        { Status: Status.Draft }
    )
);
Navigate(InfoAndCategoryScreen)

Patch関数は成功すると作成されたレコードを返します。それをSetでvarRecordに格納するのがポイントです。これにより「今どのレコードを編集しているか」が全画面で共有されます。

Navigateはセミコロンで繋いでPatchの後に書きます。先にNavigateを書くとPatchが完了する前に画面が切り替わるため、必ずPatch→Navigateの順番を守ってください。Patch関数の返り値をSetで受け取るパターンは既存レコード更新編のvarRecordとも共通する設計です。

画面遷移ボタンでPatch→Navigateの順に実行する

各画面の「次へ」ボタンにはPatchとNavigateをセットで書きます。statusはまだdraftのままです。変えるのはそのページで入力した列だけです。

Set(
    varRecord,
    Patch(
        IncidentManagement,
        varRecord,
        {
            Title: inpTitle.Text,
            Description: inpDescription.Text,
            Category: ddCategory.Selected.Value
        }
    )
);
Navigate(LocationAndPriorityScreen)

「Patchして変数を更新してからNavigate」という構造が全ページ共通です。PatchはvarRecordを返すので、Setで再びvarRecordに格納することで変数が常に最新状態に保たれます。

戻るボタンも基本的に同じ構造です。ただし「draftのときだけPatchする」という条件分岐を追加する必要があります。これは次のセクションで解説します。

draftのときだけPatchする条件分岐

提出済みのレコードを閲覧モードで開いているとき、「次へ」ボタンを押してもPatchが走ると不要なデータ変更が発生します。これを防ぐには、PatchをIf文で囲んでdraftのときだけ実行します。

If(
    varRecord.Status = Status.Draft,
    Set(
        varRecord,
        Patch(
            IncidentManagement,
            varRecord,
            { Title: inpTitle.Text }
        )
    )
);
Navigate(LocationAndPriorityScreen)

NavigateはIf文の外に置きます。これにより、draft以外のレコードを閲覧しているときもボタンで画面遷移はできます。Patchだけがdraftのときに限定されます。全ての「次へ」ボタンと「戻る」ボタンにこのパターンを適用します。

送信ボタンはstatusをopenに変えるだけ

最終画面の送信ボタンはシンプルです。途中画面でデータは全部保存済みなので、最終画面ではstatusをopenに変更するだけで済みます。

Patch(
    IncidentManagement,
    varRecord,
    { Status: Status.Open }
);
Navigate(WelcomeScreen)

全列を再度Patchする必要はありません。statusを1列だけ更新してホーム画面に戻ります。これでそのレコードはdraftではなくなるので、次回ギャラリーで開いても閲覧モードになります。

ギャラリーでdraftと非draftを見た目で区別する

一覧ギャラリーのアイコンをstatus別に切り替えると、どのレコードが編集可能かが一目でわかります。

// ギャラリー内のIconプロパティ
If(
    ThisItem.Status = Status.Draft,
    Icon.Edit,
    Icon.View
)

draftには鉛筆アイコン、提出済みには目のアイコン。これだけで「編集できるかどうか」が視覚的に伝わります。色分けを加えるとさらに直感的になります。OnSelectは両方とも共通で「varRecordにThisItemを格納してNavigate」でよいです。

表示専用モードを1か所でコントロールする

提出済みのレコードを開いたとき、全フィールドを入力不可(閲覧のみ)にする必要があります。全コントロールのDisplayModeに同じ式を書くのは非効率なので、1つのコントロールだけに式を書いて、他はそれを参照させます。

// inpTitle のDisplayModeプロパティ(ここだけ式を書く)
If(
    varRecord.Status = Status.Draft,
    DisplayMode.Edit,
    DisplayMode.Disabled
)

// 他のすべてのコントロールのDisplayModeプロパティ
inpTitle.DisplayMode

このやり方だと50個のフィールドがある画面でも変更箇所は1か所だけです。status判定ロジックを後から変えたくなったときも修正が1か所で済みます。これは地味ですが保守性を大きく上げるテクニックです。1コントロールにロジックを集約してほかはそれを参照するという考え方は、変数の一元管理と同じ発想です。

応用:空白ドラフトのクリーンアップ

新規ボタンを押してそのまま何も入力せずホームに戻ると、タイトルすら入力されていない空白のdraftレコードがテーブルに残ります。これを放置するとデータが汚れるので対処が必要です。

1つ目はギャラリーのItemsでFilter関数を使い、タイトルが空のdraftを非表示にする方法です。

Filter(
    IncidentManagement,
    Title <> "" || Status <> Status.Draft
)

2つ目はホームへの戻るボタンでRemoveIfを使い、空白draftを物理削除する方法です。確実にデータをきれいに保てますが、誤操作で入力途中のレコードが消えるリスクも考慮が必要です。どちらを選ぶかはアプリの要件次第ですが、まずはFilter方式から試すのが安全です。

まとめ

下書き保存の実装はstatus列・varRecord・条件付きPatchの3点が核心です。複雑に見えますが、「新規作成でdraftレコードを作る→途中画面でdraftのままPatch→送信時にopenに変える」という流れに沿ってコードを書けば整理できます。

下書き保存を含むPatch関数の応用パターン全体はPatch関数 応用ガイドでまとめています。各テーマの詳細解説へのリンクもそちらから辿れます。

Patch関数の基本がまだ不安な方は新規レコード作成編既存レコード更新編を先に読んでおくとスムーズです。下書き保存はPatchと変数の組み合わせの最も実践的な応用例です。一度作れてしまえば、どんな複雑な入力フォームでも同じパターンで対応できます。

Xでフォローしよう