Blazor チュートリアル - Todoアプリを構築する

# はじめに

チュートリアルを進めてTodoアプリを作ってみます。完成形は以下のような感じです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343142/f6160fbb-2e0a-3d2e-6431-0cbac2a60329.png)

下記のチュートリアルで作ったプロジェクトをベースに進めます。

https://qiita.com/firesign2023/items/7165f0977b6fb45bdd70

# Todoページを作成する

既存のプロジェクトに新しくページを追加します。

`Page`フォルダ上で右クリック→`追加`→`Razorコンポーネント`を選択します。

`Todo.razor`という名前で作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343142/adbeb4ba-fc82-a6c3-18de-8ec8c809a81f.png)

`Todo.razor`を開いて以下のように編集します。

~~~Todo.razor
@page "/todo"

<h3>Todo</h3>

@code {
}
~~~



# ナビゲーションにTodoページを追加する

サイドバーのナビゲーションから移動できるようにしましょう。

`Shared`フォルダ内の`NavMenu.razor`を開いて編集します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343142/9e00c0b6-0649-1380-f9f7-cf4d18366765.png)

~~~NavMenu.razor
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
    <ul class="nav flex-column">
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="oi oi-home" aria-hidden="true"></span> Home
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="counter">
                <span class="oi oi-plus" aria-hidden="true"></span> Counter
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="fetchdata">
                <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="todo">
                <span class="oi oi-list-rich" aria-hidden="true"></span> Todo
            </NavLink>
        </li>
    </ul>
</div>
~~~

実行してTodoページに移動できることを確認しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343142/fa837c61-f2ad-8cff-bbc0-0875d8f24e33.png)

# Todoの項目を作成する

ページを表示することができたので、あとは機能を追加していきます。
新しく`TodoItem.cs`を作成して以下のように編集します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343142/f13c58e1-64bb-a968-e733-ec12244fadd0.png)


~~~TodoItem.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace WebApplicationTrial
{
    public class TodoItem
    {
        public string? Title { get; set; }
        public bool IsDone { get; set; } = false;
    }
}
~~~

`TodoItem`を使ってページを構成します。

まずは、Todoを入力するためのテキストボックスとAddボタンを追加します。

テキストボックスに入力した値は`@bind="newTodo"`でバインドします。
Addボタンを押すと`@onclick="AddTodo"`メソッドを呼び出します。

~~~Todo.razor
<input placeholder="Something todo" @bind="newTodo" />
<button @onclick="AddTodo">Add</button>
~~~

次にボタンを押した時の動作を追加します。

`AddTodo()`：テキストボックスの入力は`newTodo`として受け取り、`List<TodoItem> todos`のリストに追加していきます。
テキストボックスの入力が何もない場合はAddボタンを押しても動きません。

~~~Todo.razor
@code {
    private List<TodoItem> todos = new();
    private string? newTodo;

    private void AddTodo()
    {
        if (!string.IsNullOrWhiteSpace(newTodo))
        {
            todos.Add(new TodoItem { Title = newTodo });
            newTodo = string.Empty;
        }
    }
}
~~~


最後に、Addボタンを押して追加した要素の表示は以下のようにします。
`Todo`のチェックボックスをクリックするとタスク完了となり、Todo横の数字が変化します

~~~Todo.razor
<h3>Todo (@todos.Count(todo => !todo.IsDone))</h3>

<ul>
    @foreach (var todo in todos)
    {
        <li>
            <input type="checkbox" @bind="todo.IsDone" />
            <input @bind="todo.Title" />
        </li>
    }
</ul>
~~~

実行して動作することを確認してみましょう。

# 機能追加

チュートリアルは以上で終わりですが、機能追加してみます。
・追加したTodoを削除するDeleteボタン

~~~Todo.razor
@page "/todo"

<h3>Todo (@todos.Count(todo => !todo.IsDone))</h3>

<ul>
    @foreach (var todo in todos)
    {
        <li>
            <input type="checkbox" @bind="todo.IsDone" />
            <input @bind="todo.Title" />
            <button class="delete-button" @onclick="() => DeleteTodo(todo)">Delete</button>
        </li>
    }
</ul>

<input placeholder="Something todo" @bind="newTodo" />
<button @onclick="AddTodo">Add</button>

@code {
    private List<TodoItem> todos = new();
    private string? newTodo;

    private void AddTodo()
    {
        if (!string.IsNullOrWhiteSpace(newTodo))
        {
            todos.Add(new TodoItem { Title = newTodo });
            newTodo = string.Empty;
        }
    }

    private void DeleteTodo(TodoItem todo)
    {
        todos.Remove(todo);
    }
}

~~~

・CSSでのスタイル適応

~~~Todo.razor

<style>

    ul {
        list-style: none;
    }

    input[type="text"] {
        padding: 5px;
        border: 1px solid #ccc;
        border-radius: 4px;
        width: 200px;
    }

        input[type="text"]:focus {
            outline: none;
            border-color: #666;
        }

    button {
        padding: 5px 10px;
        background-color: #0094ff;
        color: #fff;
        border: none;
        border-radius: 4px;
        cursor: pointer;
    }

        button:hover {
            background-color: #555;
        }

    .delete-button {
        background-color: #ccc;
        color: #fff;
        border: none;
        border-radius: 4px;
        padding: 2px 5px;
        margin-left: 5px;
        font-size: 0.8em;
        cursor: pointer;
    }

        .delete-button:hover {
            background-color: #888;
        }
</style>
~~~

実行して動くか確認してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343142/601ab361-9923-6ef2-c28b-9addae01d1ec.png)

# まとめ

チュートリアルがわかりやすくていいですね。
簡単にTodoアプリを作ることが出来ました。
引き続きBlazorについて勉強していきたいと思います。


# 参考ページ

https://learn.microsoft.com/ja-jp/training/modules/build-blazor-webassembly-visual-studio-code/


https://www.slideshare.net/jsakamoto/c-single-page-web-blazor-148130367