👋

動的に詳細項目が増えるマスター詳細フォームを Blazor で作ってみよう

2022/12/09に公開

基本的には昨日やったのと同じ方法でいけます。

https://zenn.dev/okazuki/articles/dynamic-items-form-in-blazor

ポイントはフォームの状態を表すクラスを定義して、それを元にフォームを組み立てるというところと EditContextOnValidationRequested イベントでバリデーションを行って ValidationMessageStore を更新するといった流れになります。

やってみましょう。とりあえず Name というプロパティを持っただけの親子関係をもったオブジェクトを定義します。

namespace BlazorApp4;

public class MasterDetailsForm
{
    public string? Name { get; set; }

    public List<Detail> Details { get; } = new();
}


public class Detail
{
    public string? Name { get; set; }
}

あとは、愚直に画面を組み立てるだけです。

Index.razor
@page "/"
@using System.Text.Json;
@using System.ComponentModel.DataAnnotations;

<PageTitle>Index</PageTitle>

<h1>Master details form</h1>

<EditForm EditContext="_editContext" OnValidSubmit="ValidSubmit" OnInvalidSubmit="InvalidSubmit">
    <ValidationSummary />
    <div>
        <label>
            名前
            <InputText @bind-Value="_form.Name" />
            <ValidationMessage For="() => _form.Name" />
        </label>
    </div>

    <div>
        <button @onclick="AddDetail" @onclick:preventDefault>明細の追加</button>
    </div>
    <table>
        <thead>
            <tr>
                <th>明細の名前</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var detail in _form.Details)
            {
                <tr>
                    <td>
                        <InputText @bind-Value="detail.Name" />
                        <ValidationMessage For="() => detail.Name" />
                    </td>
                </tr>
            }
        </tbody>
    </table>

    <div>
        <button type="submit">決定</button>
    </div>
</EditForm>

<hr />

<div>
    <h3>フォームデータ</h3>
    <span>@FormData</span>
</div>

<hr />

<div>
    <h3>結果</h3>
    <span style="color: red; font-size: large;">@_message</span>
</div>


@code {
    private readonly MasterDetailsForm _form = new();
    private EditContext _editContext = default!;
    private ValidationMessageStore _validationMessageStore = default!;
    private string _message = "";
    public string FormData => JsonSerializer.Serialize(_form);

    protected override void OnInitialized()
    {
        _editContext = new(_form);
        _validationMessageStore = new(_editContext);

        _editContext.OnValidationRequested += ValidationRequested;
    }

    private void AddDetail(MouseEventArgs args)
    {
        _form.Details.Add(new());
    }

    private void ValidationRequested(object? sender, ValidationRequestedEventArgs args)
    {
        _validationMessageStore.Clear();

        if (string.IsNullOrWhiteSpace(_form.Name))
        {
            _validationMessageStore.Add(() => _form.Name!, "名前は必須です");
        }

        foreach (var detail in _form.Details)
        {
            if (string.IsNullOrWhiteSpace(detail.Name))
            {
                _validationMessageStore.Add(() => detail.Name!, "詳細項目の名前は必須です");
            }
        }
    }

    private void ValidSubmit(EditContext editContext)
    {
        _message = "Valid!";
    }

    private void InvalidSubmit(EditContext editContext)
    {
        _message = "Invalid!!";
    }
}

動かしてみると初期状態ではこんな感じ。まだ明細は何もありません。

明細の追加を押すと以下のようにテキストボックスが増えていきます。

バリデーションも未入力項目だとエラーが出ます。

ちゃんと値を入れるとバリデーションエラーも消えます。

まとめ

うまくいった!

これはサンプルなので画面にバリデーションをべったり書いてるけど、本番では部品化的なことをしないとね…。

Microsoft (有志)

Discussion