LoginSignup
6
11

.NET Blazor を始めてみた

Last updated at Posted at 2023-02-12

はじめに

自分はウェブアプリを開発するのに、Onsen UI+Vue.js でアプリ開発してきましたが、.NET Blazor を使い始めました。

.NET MAUI+Blazor を始めてみた - Qiita

.NET Blazor を始めてみた

「空の」プロジェクトで始める

Visual Studio で Blazor アプリ開発するプロジェクトを新規作成するとき、「空の」プロジェクトで始めたいものです。

参考:.NET 7 で使えるようになった "空の" Blazor プロジェクトテンプレート vs. "最小の" テンプレート - Qiita

Pages/_Host.cshtml または wwwroot/index.html

自分のアプリに必要のない CSS や JavaScript ライブラリがリンクされていれば削除します。

App.razor

.NET MAUI+Blazor のプロジェクトの場合は Main.razor になります。

<Router> コンポーネントが配置されています。ページ遷移の機能を使うために残します。

MainLayout.razor

MainLayout.razor
@inherits LayoutComponentBase

<main>
    @Body
</main>

どのページに遷移してもアプリ全体で同じレイアウトにするときは、ここにコンポーネントを配置します。

Pages/Index.razor

Index.razor
@page "/"

<h1>Hello, world!</h1>

このファイルを編集します。

コンポーネントを埋込する

参考:BlazorでSPAするぞ!(2) - Component - ryuichi111stdの技術日記

コンポーネントの定義を新規作成します。拡張子 razor のファイルです。ファイル名がコンポーネント名になります。

Hello.razor
<h1>Hello, world!</h1>

作成したコンポーネントをページに埋込してみます。HTML の独自のタグとして使えます。

Index.razor
@page "/"

<Hello />

Index.razor も実はコンポーネントの定義で、MainLayout.razor で定義された @Body に埋込されています。

コンポーネント間で状態を共有する①

コンポーネントは独自のクラスです。あるコンポーネントで定義された変数やメソッドは、他のコンポーネントで使用できません。
親のコンポーネントで定義された変数を、埋込された子のコンポーネントで参照して使用したいことがあります。

子のコンポーネントの定義で変数に [Parameter] を指定します。↓

Child.razor
<p>Child</p>

<p>Count: @count<p>

@code {
    [Parameter]
    public int count { get; set; }
}

子コンポーネントでパラメータ指定した変数 count が、親コンポーネントに埋込した子コンポーネントのタグの属性 count として値を指定できます。↓

Parent.razor
<p>count: @count</p>

<button @onclick="addCount"></button>

<Child count=@count />

@code {
    int count = 0;

    void addCount()
    {
        count += 1;
    }
}

親コンポーネントで値を変えると子コンポーネントの値も変わります。

コンポーネント間で状態を共有する②

上記の例は、子のコンポーネントで値を変えても、親のコンポーネントの値は変わりません。
親コンポーネントで定義された変数を、子コンポーネントで参照して変更して、それを親コンポーネントで使用したいことがあります。

子コンポーネントで EventCallback オブジェクトを用意ます。親から受取した変数を変更したとき、このオブジェクトを Invoke() します。↓

Child.razor
<p>Child</p>

<p>Count: @count</p>

<button @onclick="addCount"></button>

@code {
    [Parameter]
    public int count { get; set; }

    [Parameter]
    public EventCallback<int> CountChanged { get; set; }

    async void addCount()
    {
        count += 1;

        await CountChanged.InvokeAsync(count);
    }
}

親コンポーネントから子コンポーネントに値を受渡するのに @bind-変数名 を使います。↓

Parent.razor
<p>count: @count</p>

<button @onclick="addCount"></button>

<Child @bind-count=@count />

以下略

@bind- を省略した書き方できるようです。↓

Parent.razor
<Child count=@count />

子コンポーネントで値を変えると親コンポーネントの値も変わります。

コンポーネント間で状態を共有する③

子コンポーネントで変更したことを親コンポーネントに伝える、他の方法があります。

子コンポーネントの EventCallback オブジェクトの名前に「On」をつけておきます(つけなくても構いません)。

Child.razor
<p>Child</p>

<p>Count: @count</p>

<button @onclick="addCount"></button>

@code {
    [Parameter]
    public int count { get; set; }

    [Parameter]
    public EventCallback<int> OnCountChanged { get; set; }

    async void addCount()
    {
        count += 1;

        await OnCountChanged.InvokeAsync(count);
    }
}

親コンポーネントから子コンポーネントにパラメータで値を渡します。子コンポーネントで用意した EventCallback オブジェクト OnCountChanged に対して、親コンポーネントでメソッド CountChanged を用意して、紐づけるようにします。このメソッドは、子コンポーネントから値を引数で受取できます。↓

Parent.razor
<p>count: @count</p>

<button @onclick="addCount"></button>

<Child count=@count OnCountChanged="CountChanged" />

@code {
    int count = 0;

    void addCount()
    {
        count += 1;
    }

    void CountChanged(int count)
    {
        this.count = count;
    }
}

コンポーネント間で状態を共有する④

コンポーネント間で状態を共有する、別の方法です。

参考:ASP.NET Core Blazor 状態管理 | Microsoft Learn

コンテナのクラスを用意して、Program.cs に追記します。

Container.cs
public class Container
{
    private int _count = 0;

    public int Count
    {
        get {
            return _count;
        }
        set {
            _count = value;
        }
    }
}
Program.cs
    中略
    builder.Services.AddSingleton<Container>();

    await builder.Build().RunAsync();

コンポーネントで @inject します。

Parent.razor
<p>Count: @container.Count</p>

<button @onclick="addCount"></button>

<Child />

@inject Container container

@code {
    void addCount()
    {
        container.Count += 1;
    }
}
Child.razor
<p>Child</p>

<p>Count: @container.Count</p>

<button @onclick="addCount"></button>

@inject Container container

@code {
    void addCount()
    {
        container.Count += 1;
    }
}

container がアプリ全体で参照できる大域変数のように使えます。
ただし、container.count に代入しても、画面の表示が変わりません。↑

コンテナで Action オブジェクトを用意して、参照した変数を変更したとき、このオブジェクトを Invoke() します。↓

Container.cs
public class Container
{
    中略
    public event Action? OnChange;
    中略
    public int Count
    {
        中略
        set
        {
            _count = value;

            OnChange?.Invoke();
        }
    }

コンテナを @inject しているコンポーネントは、自分の StateHasChanged メソッドをコンテナの Action オブジェクトにセットします。これで、コンテナの値が変更されたとき、コンポーネントの StateHasChanged メソッドが実行されて、画面が再描画されます。↓

Parent.razor
中略
@implements IDisposable

@code {
    中略
    protected override void OnInitialized()
    {
        container.OnChange += this.StateHasChanged;
    }
    public void Dispose()
    {
        container.OnChange -= this.StateHasChanged;
    }
Child.razor
中略
@implements IDisposable

@code {
    中略
    protected override void OnInitialized()
    {
        container.OnChange += this.StateHasChanged;
    }
    public void Dispose()
    {
        container.OnChange -= this.StateHasChanged;
    }

コンテナの OnChangedStageHasChanged をセットするのはコンポーネントの OnInitialized で、逆に Dispose のタイミングで OnChanged から StageHasChanged を外さないといけません。↑

ページ遷移する

参考:BlazorでSPAするぞ!(6) - Routing - ryuichi111stdの技術日記

App.razor<Router> が初期化されています。MainLayout.razor@Body に埋込されるページを切替します。

ページの定義を新規作成します。@page ディレクティブで URL を指定します。

Next.razor
@page "/next"

<h1>Next Page</h1>

このページに遷移できるようにします。

Index.razor
@page "/"

<a href="next">Next</a>
<NavLink href="next">Next</NavLink>
<button @onclick="GoToNext">Next</button>

@inject NavigationManager nm

@code {
	private void GoToNext()
	{
		nm.NavigateTo("next");
	}
}
6
11
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
6
11