1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Blazor + Syncfusion】SfDialogで削除確認ダイアログを作ってみて感じたこと

1
Posted at

はじめに

担当: 石井(入社4年目🐈)

業務アプリでは、処理の前に確認ダイアログを出した方がいい場面が色々あります。

例えば、削除処理の前には

本当に削除してよろしいですか?

のような確認ダイアログがあった方が良いでしょう。

削除確認がない画面だと、次のような事故が起こりやすくなります。

  • 一覧の行を見間違えて、別のデータを削除してしまう
  • 連打や誤クリックで意図せず削除処理が走る
  • 間違って削除してしまった場合に気付きにくい

うっかり消してしまった1件のデータが業務上かなり大事な情報だった!なんてこともあるので、削除前の確認はとても重要です。

今回は、Syncfusionの SfDialog を使って、一覧画面の削除ボタンから確認ダイアログを表示し、OKなら削除、キャンセルなら何もしない、というシンプルな流れを作ってみます。

今回作るもの

今回作るのは、以下のような画面です。

  • 一覧に削除ボタンを表示する
  • 削除ボタンを押すと確認ダイアログを表示する
  • OKを押したら対象データを削除する
  • キャンセルを押したら何もしない

実務で使う場合は削除前チェックや権限制御なども必要になりますが、今回はまず画面側の流れを中心に見ていきます。

SfDialogとは

SfDialog は、Syncfusion Blazorでダイアログを表示するためのコンポーネントです。

確認メッセージを出したり、入力フォームをポップアップで表示したり、処理結果を通知したりするときに使えます。

削除確認のようにユーザーに一度操作を止めて判断してもらいたい場面では、モーダルダイアログにしておくと分かりやすいです。

SyncfusionのSfDialogを使うメリット

確認ダイアログ自体は自作もできますが、SfDialog を使うと次の点が扱いやすいです。

  • @bind-Visible で表示状態をそのまま管理できる
  • SfGrid と組み合わせた一覧画面の実装に自然になじむ
  • モーダル表示や閉じる挙動をコンポーネント側に任せられる

「削除前に確認する」という要件に対して、実装コストを抑えながら形にしやすいのが利点です。

前提

この記事では、Blazor Web Appのプロジェクトが作成済みの状態から進めます。

Syncfusionの基本設定として、以下が追加されている想定です。

Components/_Imports.razor
@using Syncfusion.Blazor
@using Syncfusion.Blazor.Popups
@using Syncfusion.Blazor.Grids

Program.cs にはSyncfusionのサービス登録を追加します。

Program.cs
using Syncfusion.Blazor;

builder.Services.AddSyncfusionBlazor();

テーマCSSやスクリプトも追加しておきます。

Components/App.razor
<link href="_content/Syncfusion.Blazor.Themes/fluent2.css" rel="stylesheet" />
<script src="_content/Syncfusion.Blazor.Core/scripts/syncfusion-blazor.min.js" type="text/javascript"></script>

Dialogを使う場合は、Syncfusion.Blazor.Popups パッケージが必要です。

以下のコマンドをターミナル(コマンドライン)で実行します。

dotnet add package Syncfusion.Blazor.Popups

まずはSfDialogを表示してみる

最初に、シンプルな確認ダイアログだけを表示してみます。

sfDialog-simple-Dialog.png

Components/Pages/DialogSample.razor
@page "/dialog-sample"

<h3>削除確認ダイアログ</h3>

<button class="e-btn e-danger" @onclick="OpenDialog">
    削除
</button>

<SfDialog Width="400px" IsModal="true" ShowCloseIcon="true" Header="削除確認" @bind-Visible="IsDeleteDialogVisible">
    <DialogTemplates>
        <Content>
            <p>このデータを削除してもよろしいですか?</p>
        </Content>
        <FooterTemplate>
            <button class="e-btn e-danger" @onclick="ConfirmDelete">OK</button>
            <button class="e-btn" @onclick="CancelDelete">キャンセル</button>
        </FooterTemplate>
    </DialogTemplates>
</SfDialog>

@code {
    private bool IsDeleteDialogVisible { get; set; } = false;

    private void OpenDialog()
    {
        IsDeleteDialogVisible = true;
    }

    private void ConfirmDelete()
    {
        // ここで削除処理を行う
        IsDeleteDialogVisible = false;
    }

    private void CancelDelete()
    {
        IsDeleteDialogVisible = false;
    }
}

IsDeleteDialogVisibletrue のときにダイアログが表示され、false にすると閉じます。

private bool IsDeleteDialogVisible { get; set; } = false;

ボタンを押したときに true にするだけなので、表示・非表示の切り替えはかなり分かりやすいです。

private void OpenDialog()
{
    IsDeleteDialogVisible = true;
}

確認ダイアログのようなUIは、自前で作ろうとすると背景のオーバーレイや中央配置、閉じる処理などを考える必要があります。

SfDialog を使うと、そのあたりをコンポーネントに任せられるので、実装したい処理に集中しやすいと感じました。

SfGridSfDialog を組み合わせる

次に、一覧画面と組み合わせてみます。

今回はサンプルとして、社員データのような簡単な一覧を用意します。

public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public string Department { get; set; } = string.Empty;
}

画面側では、以下のようなデータを持たせます。

private List<Employee> Employees =
[
    new Employee { Id = 1, Name = "山田", Department = "開発部" },
    new Employee { Id = 2, Name = "佐藤", Department = "営業部" },
    new Employee { Id = 3, Name = "田中", Department = "管理部" }
];

削除対象を保持するための変数も用意しておきます。

private Employee? SelectedEmployee;

削除ボタンを押したときに、この SelectedEmployee に削除対象のデータを入れてからダイアログを表示する流れです。

では、SfGrid の一覧に削除ボタンを表示して、クリックした行を削除できるようにしてみます。

Components/Pages/DeleteDialogSample.razor
@page "/delete-dialog-sample"
@rendermode InteractiveServer

<h3>社員一覧</h3>

<SfGrid DataSource="@Employees" TValue="Employee" Width="600px">
    <GridColumns>
        <GridColumn Field="@nameof(Employee.Id)" HeaderText="ID" Width="80" TextAlign="TextAlign.Right" />
        <GridColumn Field="@nameof(Employee.Name)" HeaderText="名前" Width="160" />
        <GridColumn Field="@nameof(Employee.Department)" HeaderText="部署" Width="160" />
        <GridColumn HeaderText="操作" Width="120">
            <Template>
                @{
                    var employee = context as Employee;
                }
                <button class="e-btn e-danger" @onclick="() => OpenDeleteDialog(employee)">削除</button>
            </Template>
        </GridColumn>
    </GridColumns>
</SfGrid>

<SfDialog Width="400px" IsModal="true" ShowCloseIcon="true" Header="削除確認" @bind-Visible="IsDeleteDialogVisible">
    <DialogTemplates>
        <Content>
            @if (SelectedEmployee is not null)
            {
                <p>「@SelectedEmployee.Name」さんのデータを削除してもよろしいですか?</p>
            }
        </Content>
        <FooterTemplate>
            <button class="e-btn e-danger" @onclick="ConfirmDelete">OK</button>
            <button class="e-btn" @onclick="CancelDelete">キャンセル</button>
        </FooterTemplate>
    </DialogTemplates>
</SfDialog>

@code {
    private bool IsDeleteDialogVisible { get; set; } = false;
    private Employee? SelectedEmployee;
    private List<Employee> Employees =
    [
        new Employee { Id = 1, Name = "山田", Department = "開発部" },
        new Employee { Id = 2, Name = "佐藤", Department = "営業部" },
        new Employee { Id = 3, Name = "田中", Department = "管理部" }
    ];

    private void OpenDeleteDialog(Employee? employee)
    {
        if (employee is null)
        {
            return;
        }

        SelectedEmployee = employee;
        IsDeleteDialogVisible = true;
    }

    private void ConfirmDelete()
    {
        if (SelectedEmployee is not null)
        {
            Employees = Employees.Where(e => e.Id != SelectedEmployee.Id).ToList();
        }

        SelectedEmployee = null;
        IsDeleteDialogVisible = false;
    }

    private void CancelDelete()
    {
        SelectedEmployee = null;
        IsDeleteDialogVisible = false;
    }

    public class Employee
    {
        public int Id { get; set; }
        public string Name { get; set; } = string.Empty;
        public string Department { get; set; } = string.Empty;
    }
}

これで、一覧の削除ボタンを押すと確認ダイアログが表示されます。

sfDialog-delete.gif

OKを押すと対象データを削除し、キャンセルを押すと何もせずに閉じます。

今回実装してみて感じた3つのポイント

ここからは、今回実装してみて設計上大切だと感じたポイントを3つ紹介します。

1. 削除操作を「押下」と「実行」に分ける

ボタンを押した時点では実行せず、まず対象を保持して確認ダイアログを表示します。

こちらは先ほどのサンプルの、削除ボタンを押したときの処理内容です。

private void OpenDeleteDialog(Employee? employee)
{
    if (employee is null)
    {
        return;
    }

    SelectedEmployee = employee;
    IsDeleteDialogVisible = true;
}

ここでは、削除対象を SelectedEmployee に入れて IsDeleteDialogVisibletrue にし、ダイアログを開いています。

実際に削除するのはOKボタンを押したタイミングです。

private void ConfirmDelete()
{
    if (SelectedEmployee is not null)
    {
        Employees = Employees.Where(e => e.Id != SelectedEmployee.Id).ToList();
    }

    SelectedEmployee = null;
    IsDeleteDialogVisible = false;
}

このように 「削除ボタン押下」と「実際の削除処理」を分ける ことで、
誤操作のリスクを減らしつつ、処理の流れを明確にできます。

SfDialog では @bind-Visible で表示状態を管理できるため、
削除ボタン押下時の処理と実際の削除処理を分けて実装しやすくなります。

2. 削除対象と実行後の影響が伝わる文言にする

確認ダイアログでは、「誰の何を削除するか」が伝わる文言にしておくことが重要です。

sfDialog-advanced-Dialog.png

<Content>
    <p>「@SelectedEmployee.Department @SelectedEmployee.Name」さんのデータを削除してもよろしいですか?</p>
</Content>

のように、IDや所属など他の情報も併記すると誤認を減らせます。

また、必要に応じて「実行後にどうなるか」まで補足しておくと、ユーザーが判断しやすくなります。

例えば、論理削除で後から復元できる場合と、物理削除で完全に消える場合では、伝え方を変えた方が親切です。

このデータは削除後も管理者が復元できます。
この操作は取り消せません。

のように、判断材料を明示しておくとユーザーも安心です。

3. DialogTemplatesで本文と操作の責務を分ける

メッセージ差し替えとボタン変更を別々に扱える構成にしておくことも、保守性の面で大切です。

SfDialog では DialogTemplatesContentFooterTemplate を分けて書くことができるので、
表示内容と操作ボタンの責務を分離しやすくなります。

<DialogTemplates>
    <Content>
        @* ダイアログの本文 *@
        <p>「@SelectedEmployee.Name」さんのデータを削除してもよろしいですか?</p>
    </Content>
    <FooterTemplate>
        @* ボタン表示 *@
        <button class="e-btn e-danger" @onclick="ConfirmDelete">OK</button>
        <button class="e-btn" @onclick="CancelDelete">キャンセル</button>
    </FooterTemplate>
</DialogTemplates>

この分け方にしておくと、本文の変更とボタン操作の変更を別々に扱えるため、修正時の影響範囲を追いやすくなります。

逆に、役割を分けずに1か所へ寄せてしまうと、表示文言と操作が混ざって見通しにくくなります。

<DialogTemplates>
    <Content>
        <p>「@SelectedEmployee.Name」さんのデータを削除してもよろしいですか?</p>
        <button class="e-btn e-danger" @onclick="ConfirmDelete">OK</button>
        <button class="e-btn" @onclick="CancelDelete">キャンセル</button>
    </Content>
</DialogTemplates>

この形でも動作はできますが、後から「メッセージだけ変えたい」「ボタンを1つ追加したい」となったときに、編集箇所が追いづらくなります。

また、SfDialog を使わずに同等の機能を作る場合は、表示/非表示の制御に加えて、

  • オーバーレイ
  • フォーカス移動
  • ESCキーや背景クリックで閉じる挙動

などを個別に設計する必要があり、実装と保守の負担が増えやすいです。

今回は扱わなかったこと

今回は紹介記事として、基本的な削除確認ダイアログの流れに絞って紹介しました。

そのため、実装の詳細までは触れていない項目もあります。

今回扱わなかったのは、主に次のような実装面です。

  • DBへの削除処理
  • API呼び出し
  • 削除前チェック(参照中・承認済みなどの業務ルール判定)
  • 権限制御

このあたりは重要なので、案件に合わせて別途作り込む前提になります。

まとめ

今回は、Syncfusion Blazorの SfDialog を使って削除確認ダイアログを作ってみました。

今回実装してみて、特に大切だと感じたのは次の3点です。

  • 削除操作を2段階に分ける
  • 削除対象と実行後の影響が伝わる文言を表示する
  • 本文と操作ボタンの責務を分ける

SfDialog を使うことで、この3点を押さえて削除確認フローを組み立てることができました。

自前実装でも同じことは可能ですが、表示制御やモーダル周辺の挙動を個別に扱う場面が増えるため、SfDialog を土台に組み立てる方が進めやすいです。

これからBlazorやSyncfusionを触る方の参考になれば嬉しいです!

参考リンク集

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?