.NET 6 で動的にコンポーネントをレンダリングする機能が追加されたので試してみる
日付的に .NET 6 の正式リリース前の最後のリリースの RC2 が出てきても不思議じゃないですね!ということで引き続き .NET 6 の Blazor (WebAssembly しか自分は検証してません) の新機能を試してみたいと思います。
今日は、既に日本語ドキュメントページがある DynamicComponent
について試してみようと思います。
本文
ということでやっていきます。といってもこのコンポーネント自体は非常にシンプルで DynamicComponent
に Type
プロパティでレンダリングしたいコンポーネントの型を設定して Parameters
プロパティで設定したいプロパティを IDictionary<string, object>
で設定します。
全然ダイナミックじゃないですが以下のようにすることで Counter
コンポーネントを DynamicComponent
を使って表示できます。Blazor のプロジェクトを作って Index.razor に以下のように書いてみました。
@page "/"
<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
<DynamicComponent Type="typeof(Counter)" />
実行すると以下のように表示されます。Hello, world! の下に Counter
コンポーネントが表示されます。
次にパラメーターを渡してみます。まずは Counter
コンポーネントに InitialValue
というパラメーターを追加して初期値を設定できるようにしてみました。
@page "/counter"
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
[Parameter]
public int InitialValue { get; set; }
private int currentCount = 0;
protected override void OnParametersSet()
{
base.OnParametersSet();
currentCount = InitialValue;
}
private void IncrementCount()
{
currentCount++;
}
}
DynamicComponent
の方で、この InitialValue
プロパティを設定してみます。最初に書いたように Parameters
プロパティに IDictionary<string, object>
で渡します。なので渡すだけなら以下のように書けます。
@page "/"
<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
<DynamicComponent Type="typeof(Counter)" Parameters="@(new Dictionary<string, object> { ["InitialValue"] = 100 })" />
Parameters
プロパティで直接 Dictionary<string, object>
を生成して InitialValue
に 100 を設定しています。実行すると以下のように表示された段階で既にカウンターの値が 100 になっています。
最後に Instance
プロパティについて紹介します。これは DynamicComponent
でレンダリングしている実際のコンポーネントのインスタンスを取得します。例えば Counter
コンポーネントに値をリセットする Reset
メソッドを追加して、それを DynamicComponent
から呼んでみましょう。
まずは Reset
メソッドを追加します。
@page "/counter"
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
[Parameter]
public int InitialValue { get; set; }
private int currentCount = 0;
// リセットメソッドを追加!
public void Reset()
{
currentCount = InitialValue;
StateHasChanged();
}
protected override void OnParametersSet()
{
base.OnParametersSet();
currentCount = InitialValue;
}
private void IncrementCount()
{
currentCount++;
}
}
そして DynamicComponent
を @ref
を使って変数に保存して、そこから Instance
プロパティ経由で Counter
のインスタンスを取得して Reset
メソッドを呼び出しています。
@page "/"
<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
@* @ref で DynamicComponent をフィールドの変数に保持 *@
<DynamicComponent Type="typeof(Counter)" Parameters="@(new Dictionary<string, object> { ["InitialValue"] = 100 })" @ref="_counter" />
<button @onclick="ResetButton_Click">Reset</button>
@code {
private DynamicComponent? _counter;
private void ResetButton_Click()
{
// ボタンが押されたら DynamicComponent から Counter のインスタンスをとって Reset を呼ぶ
if (_counter?.Instance is Counter c)
{
c.Reset();
}
}
}
実行すると以下のように動きます。Reset ボタンを押すと、ちゃんと Counter
コンポーネントの Reset
メソッドが呼ばれていることがわかると思います。
動的にしてみよう
さて、今まで DynamicComponent
を不便なコンポーネントのように使ってきました!これまでの例では、そのまま Counter
コンポーネントを置いた方がいいのでは…という感じだったのですが、本来は、データなどに応じて、その場に表示するコンポーネントを動的に切り替えるというのが意図された使い方です。
今までは、それを実行しようとすると以下のように自分で切り替わる可能性のある種類の数だけ if
などで分岐する必要がありました。[1]
@foreach (var item in items)
{
@* 表示に使うコンポーネントの種類を ComponentType というプロパティで列挙体で管理してる想定 *@
@if (item.ComponentType == ComponentType.Foo)
{
<Foo Value="item.Value" />
}
else if (item.ComponentType == ComponentType.Bar)
{
<Bar Value="item.Value" />
}
else if (...)
{
@* 表示する可能性のあるコンポーネントの種類だけ続ける *@
}
}
DynamicComponent
を使うと表示するデータとあわせて、表示に使うコンポーネントの型を変数などで管理すればよくなります。例えば先ほどの例を DynamicComponent
を使って書き換えると以下のような感じになるはずです。[1:1]
@* items の方に表示に使うコンポーネントの型を入れておく *@
var items = new[]
{
new Item(Value = "value1", ComponentType = typeof(Foo)),
new Item(Value = "value2", ComponentType = typeof(Bar)),
new Item(Value = "value3", ComponentType = typeof(Baz)),
};
@* 表示は DynamicComponent でサクッと *@
@foreach (var item in items)
{
<DynamicComponent Type="item.ComponentType" Parameters="@(new Dictionary<string, object> { ["Value"] = item.Value })" />
}
すっきりですね。
わからなかった点
軽く調べた感じだと DynamicComponent
の Parameters
プロパティ経由で EventCallback
型のパラメーターを渡す方法がわかりませんでした!単純にデリゲートを渡すと型変換に失敗するし EventCallback
のインスタンス作るときのコンストラクタ引数はわからないし…。GitHub の Issue を見てみたけど特にイベントを渡す方法に関する言及は見つけることができませんでした。
探し方が悪いだけ、かもしれませんが…。
まとめ
ということで .NET 6 で追加される DynamicComponent
を試してみました。イベントも設定がさくっとできたらいいんですけどね…。追々調べてみようと思います。
Discussion