🐥note.

小鳥とMicrosoft <3 なエンジニアの技術Blog📚

Blazor - TodoAppで学ぶComponentへのParameter Binding

BlazorのComponentへのParameter Bindingについて学ぶため簡単なTodoアプリを作りました。
今後暫くはこのアプリに手を加える形でBlazorのお勉強をしたいと思います。

github.com

Microsoft Docsで言うとこのあたり

docs.microsoft.com

Demo

こんな感じです。

f:id:piyo_esq:20191016062801g:plain
Demo

Index.razor

Indexページに<TodoList>というComponentを追加します。
<TodoList>にはパラメータとしてTodoのリストを与え、このデータを<TodoList>以下で描画します。

@page "/"

<h1>Blazor Todo</h1>

<TodoList Todos="@Todos" />

@code {
    IList<Todo> Todos;
    protected override void OnInitialized()
    {
        Todos = new List<Todo>()
            {
                new Todo() { Id = Guid.NewGuid(),  Content = "犬をしまう" },
                new Todo() { Id = Guid.NewGuid(),  Content = "お風呂に水をためる" },
                new Todo() { Id = Guid.NewGuid(),  Content = "水・食料を買い込む" },
                new Todo() { Id = Guid.NewGuid(),  Content = "避難先を確認する" },
            };
    }
}

TodoList.razor

Index.razorから受け取ったTodoを<TodoEntry>で描画します。
<TodoEntry>にはDeleteボタン押下時に実行するAction<Guid>な関数を与えています。
子Componentで行うデータ操作を全てをここに集約している感じですね。

Todo追加用に<NewItemEntry>というComponentも追加しています。

@using System.Linq;

<table class="table">
    <thead>
        <tr>
            <th>Done</th>
            <th>Todo</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var todo in Todos)
        {
            <TodoEntry Todo="@todo" OnDeleteTodoItem="@OnDeleteTodoItem" />
        }
    </tbody>
</table>

<NewItemEntry OnAddNewTodoItem="@OnAddNewTodoItem" />

@code {
    [Parameter]
    public IList<Todo> Todos { get; set; }

    void OnAddNewTodoItem(string newContent)
    {
        if (Todos != null)
        {
            Todos.Add(new Todo() { Id = Guid.NewGuid(), Content = newContent });
            StateHasChanged();
        }
    }

    void OnDeleteTodoItem(Guid id)
    {
        if (Todos != null)
        {
            var item = Todos.FirstOrDefault(f => f.Id == id);
            Todos.Remove(item);
            StateHasChanged();
        }
    }
}

TodoEntry.razor

<TodoList>から受け取ったTodoモデルを描画します。
Checkboxを選択するとtrClassを変更し、テーブルのRowのCSSクラスを変更します。

Deleteボタン押下時は<TodoList>から受け取ったAction<Guid>を実行します。

<tr class="@trClass">
    <td><input type="checkbox" @bind="todo.IsDone"/></td>
    <td>@todo.Content</td>
    <td><button @onclick="DeleteTodoItem">Delete</button></td>
</tr>

@code {
    [Parameter]
    public Todo todo {get; set;}
    [Parameter]
    public Action<Guid> OnDeleteTodoItem { get; set;}

    protected string trClass => todo.IsDone ? "table-todo-entry" : null;

    void DeleteTodoItem()
    {
        if(OnDeleteTodoItem != null)
        {
            OnDeleteTodoItem(todo.Id);
        }
    }
}

NewItemEntry.razor

新しいTodoを追加するComponentです。
テキストボックスでEnterを押下するか、Add New Itemボタンを押下すると<TodoList>から受け取ったAction<string>を実行します。

<div>
    <span>New Todo Item</span>
    <input @bind-value="content" @bind-value:event="oninput" @onkeypress="KeyPress"/>
    <button @onclick="AddNewItem">Add New Item</button>
</div>

@code {
    string content { get; set; }

    [Parameter]
    public Action<string> OnAddNewTodoItem { get; set; }

    void AddNewItem()
    {
        if (OnAddNewTodoItem != null)
        {
            OnAddNewTodoItem(content);
            content = string.Empty;
        }
    }

    void KeyPress(KeyboardEventArgs e)
    {
        if (e.Key == "Enter")
        {
            AddNewItem();
        }
    }
}

はまったとこ

Sharedフォルダ以下の名前空間

Shared\Todo以下にComponentを置いたのですが、名前空間は自動的に{アプリ名}.Shared.{フォルダ名}になるんですね。
※今回の場合BlazorTodo.Shared.Todoです。

ということで_Import.razor名前空間を追加しました。

@using System.Net.Http
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.JSInterop
@using BlazorTodo
@using BlazorTodo.Data
@using BlazorTodo.Shared

@using BlazorTodo.Shared.Todo // 追加!

おわり

Vue.js並みに簡単に書けちゃいますね。