2021年1月11日月曜日

Blazor向けUIコンポーネントMatBlazorでマテリアルデザイン

どうも、もりもりです。

前回は .NETでSPA(SinglePageApplication)を実現できるWebフレームワークの BlazorWebAssemblyでTodo画面を作成しました。 今回はMatBlazorを使って、そのTodo画面をマテリアルデザインにしてみました。

MatBlazor使ったらどうなるの?

こんな感じの画面が

こんな感じの画面になっちゃいます。

ソースコードは下記に置いてます。

前準備

まずは前準備としてMatBlazorのパッケージを追加し、使用できるように設定していきます。

◆ パッケージの追加

下記のコマンドでパッケージを追加します。念のためcsprojに追加されていることを確認しておきましょう。

$ dotnet add package MatBlazor
◆ _imports.razorを編集

下記を追加します。

_imports.razor

@using MatBlazor
◆ wwwroot/index.htmlを編集

下記を追加します。

index.html

<body>
    <div id="app">Loading...</div>

    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>
    <script src="_framework/blazor.webassembly.js"></script>
    <!-- 下記を追加 -->
    <script src="_content/MatBlazor/dist/matBlazor.js"></script>
</body>

これで準備は整いました。

Todoアイテム入力欄

ここではチェックボックス、入力欄、ボタンにMatBlazorを使用しています。

TodoMat.razor

@page "/todomat"

<style>
    .new-item-text-field > label.mat-text-field > span {
        margin-left: 30px;
    }
</style>

<h1>Todo</h1>

<div class="new-item-text-field" style="display: flex; position: relative; align-items: center;">
    <MatCheckbox Style="z-index: 1; position: absolute; left: 0;" 
                 @bind-Value="allChecked" Disabled="@(!TodoItemExists())"></MatCheckbox>
    <MatTextField @bind-Value="newItem" Label="What needs to be done?"
                  OnKeyDown="@Enter" OnInput="@OnInput"
                  Style="z-index: 0; width: 500px; padding-left: 50px;"></MatTextField>
    <MatButton OnClick="@AddItem" Outlined="true">add</MatButton>
</div>

MatBlazorのドキュメントに使用例がたくさん載ってるのでさらっと説明を。

◆ MatCheckbox

チェックボックスのコンポーネント。前回作成したTodo画面では入力欄のとこにチェックボックスを付けてなかったのですが追加しました。

Name Description
@bind-Value バインドする値。true / false
Disabled 無効にするかどうか。true / false

@bind-Valueには、プロパティallCheckedをバインド。 コメントの通りです。

Disabledには、メソッドTodoItemExists()の戻り値をバインド。

TodoMat.cs

// newItemのチェック状態
private bool allChecked
{
    get
    {
        // 未完了アイテムがない場合にチェックON、一つでもあればチェックOFF
        return !allTodos.Any(x => !x.IsDone);
    }
    set
    {
        // チェック状態に合わせてTodoアイテムすべてのチェック状態を変更
        allTodos.ForEach(x => x.IsDone = value);
    }
}
        
/// <summary>
/// Todoアイテムが存在するかどうか
/// </summary>
/// <returns></returns>
private bool TodoItemExists()
{
    return allTodos.Any();
}
◆ MatTextField

テキストフィールドのコンポーネント。

Name Description
@bind-Value バインドする値。入力した値を保持。
Label 入力項目名。今回はPlaceholder的な使い方してます。
HelperTextというのが別に用意されているので本来はこちらの方がいいのかな?
OnKeyDown キーダウンイベント。
OnInput 入力イベント。

ただ1個だけ面倒だったのがあります。

MatTextFieldのLabel「What needs to be done?」の文字の位置。 CheckBoxとTextFieldを重ねてるので入力内容はStyle属性で paddingを指定してやればズラせたのですが Labelの文字位置は用意されてる属性では触れそうになかったので Styleを強引にイジってます。(上記ソースの4-6行目) これが正解なのかどうかわかりませんがまたどっかで時間作って確認してみます。

◆ MatButton

ボタンのコンポーネント。

Name Description
OnClick クリックイベント。
Outlined ボタンの枠を表示するかどうか。

Todoアイテムリスト

リスト用コンポーネント。MatListとMatListItemを使用してます。今回はStyleのみ指定。

TodoMat.razor

<style>
    .completed-todo-item {
        text-decoration: line-through;
        color: lightgray !important;
    }
</style>
・・・
・・・
<MatList Style="width: 500px">
    @foreach (var item in todosForDisplay)
    {
        <MatListItem Style="padding: 0;">
            <MatCheckbox Style="z-index: 1;" @bind-Value="item.IsDone"></MatCheckbox>
            <MatStringField Style="z-index: 0; width: 100%; position: absolute; padding-left: 50px;" 
                            InputClass="@GetCompletedTodoItemCssClass(item.IsDone)" 
                            @bind-Value="item.Content"></MatStringField>
        </MatListItem>
    }
</MatList>

MatListItem内ではMatCheckboxとMatStringFieldを使用しています。 MatStringFieldはMatTextFieldと違ってString型専用のコンポーネントっぽいです。 MatTextFieldはintやDateTimeなどの型がバインドできます。

◆ MatStringField

String専用の入力用コンポーネント。

Name Description
InputClass input要素のCSSクラス。

Styleに「completed-todo-item」を用意し、InputClassに チェック状態によって異なるStyle名を返すメソッド「GetCompletedTodoItemCssClass()」をバインドしています。

TodoMat.cs

/// <summary>
/// Todo完了アイテムCSSクラス名を取得
/// </summary>
/// <param name="isDone">完了フラグ</param>
/// <returns>クラス名</returns>
private string GetCompletedTodoItemCssClass(bool isDone)
{
    return isDone ? "completed-todo-item" : "";
}

リスト下のボタン群

ボタン用のコンポーネント。

TodoMat.razor

@allTodos.Count(x => !x.IsDone) items left
<MatButton OnClick="@ShowAll" Outlined="true" Style="margin-left: 20px">All</MatButton>
<MatButton OnClick="@ShowActive" Outlined="true">Active</MatButton>
<MatButton OnClick="@ShowCompleted" Outlined="true" Style="margin-right: 20px">Completed</MatButton>
<MatButton OnClick="@ClearCompleted" Outlined="true" 
           Disabled="@(!CompletedTodoItemExists())">Clear completed</MatButton>

TodoMat.cs

/// <summary>
/// 完了済みのTodoアイテムが存在するかどうか
/// </summary>
/// <returns></returns>
private bool CompletedTodoItemExists()
{
    return allTodos.Any(x => x.IsDone);
}

さいごに

使ってみた感じ、CSSの知識がない人にとっては嬉しいとは思いますが Webの知識がある人からすると物足りなさそうな感じがします。 ちょっと細かいことに拘るとCSSの知識も必要になってきます。 ドキュメントはわかりやすかったのですんなり使えました。 他にもBlazor向けのコンポーネントで良さそうなのがあったらまた紹介したいと思います。

以上、もりもりでした。

0 件のコメント:

コメントを投稿