💡

Blazorでの属性スプラッティング活用

に公開

はじめに

Blazorについて勉強し始めたとき、属性スプラッティングという機能を知り、大変感動しました。あの感動をもう一度!ということで、Blazorでの属性スプラッティングについてご紹介いたします。

Blazorとは

本記事を読まれる方は、Blazorについてご存じとは思いますが、一応、定義としてご説明をばと、以下引用。

Blazor は、HTML、CSS、C# をベースにした最新のフロントエンド Web フレームワークであり、Web アプリ構築の迅速化に役立ちます。Blazor では、クライアントとサーバーの両方から実行できる再利用可能コンポーネントを使用して Web アプリを構築し、優れた Web エクスペリエンスを提供できます。

https://dotnet.microsoft.com/ja-jp/apps/aspnet/web-apps/blazor

私の偏見も含めてざっくりまとめますと、
なんとなんと、フロントエンドもバックエンドも同じプロジェクト内でC#で記述できちゃうよ、という感じです。便利!

属性スプラッティングとは

属性スプラッティング(Attribute Splatting)とは、コンポーネントやHTML要素に対して、あらかじめ用意した複数の属性をまとめて適用するための仕組みです。

以下のように@attributesにDictionalyを渡して、コンポーネントのParameter属性の変数に値を渡します。

.razor.cs
// .razor.cs
private readonly Dictionary<string, object> _formAttribute = [];
.razor
@*.razor*@
<span>
    <ComponentTest1 @attributes = "@_formAttribute" />
</span>

何言ってんだって感じですけども、要するに、
@attributesにDictionary<string,T>を渡すと、key文字列と一致する変数にvalueが格納されますよ、っていうことです。

環境

  • .NET 8
  • Blazor WASM

テストページ準備

以下のようにTestPageとComponentTest1を追加したBlazorサンプルプロジェクトがあるとします。
TestPageはNavMenuに追加表示し、TestPageを表示するとします。

さらに、TestPageからComponentTest1というコンポーネントを表示するとします。

TestPage

TestPage.razor
@*TestPage.razor*@
@page "/mytest"
<PageTitle>MyTest</PageTitle>
<span>
    <ComponentTest1 @attributes = "@_formAttribute" />
</span>
TestPage.razor.cs
// TestPage.razor.cs
public partial class TestPage
{
    private readonly Dictionary<string, object> _formAttribute = [];

    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();
    }
}

ComponentTest1

ComponentTest1.razor
@*ComponentTest1.razor*@
<h3>ComponentTest1</h3>  
ComponentTest1.razor.cs
// ComponentTest1.razor.cs
public partial class ComponentTest1
{
}

現状は何も属性スプラッティングで変数値を渡してないので、ComponentTest1のみ表示されます。

動作確認

基本的な属性スプラッティング使い方

ComponentTest1に簡単な文字列を属性スプラッティングで値を渡して表示します。
_formAttributeにkeyを変数名、valueは変数値を格納して、@attributesで属性スプラッティングで渡します。
以下は、簡単な例として、ComponentTest1のMsgという変数に、"Test Message"という文字列を渡します。

TestPage

TestPage.razor
@*TestPage.razor*@
@page "/mytest"
<PageTitle>MyTest</PageTitle>
<span>
    <ComponentTest1 @attributes = "@_formAttribute" />
</span>
TestPage.razor.cs
// TestPage.razor.cs
public partial class TestPage
{
    private readonly Dictionary<string, object> _formAttribute = [];

    protected override async Task OnInitializedAsync()
    {
        // ComponentTest1のMsgというパラメータ属性のついた変数にTest Messageという文字列を渡す用。
        _formAttribute.Add("Msg", "Test Message");
        await base.OnInitializedAsync();
    }
}

ComponentTest1

ComponentTest1.razor
@*ComponentTest1.razor*@
<h3>ComponentTest1</h3>  
@*メッセージの変数を表示*@
<span>@Msg</span>
ComponentTest1.razor.cs
// ComponentTest1.razor.cs
public partial class ComponentTest1
{
    [Parameter]
    public string? Msg { get; set; }
}

実行結果:
Msgに"Test Message"という文字列が属性スプラッティングによって格納され、TestPage上のComponentTest1に表示されました。

応用的な属性スプラッティング

Blazorの属性スプラッティングは、単一的な変数のみでなく、ジェネリクスのオブジェクト等も渡すことができます。
以下は、属性スプラッティングでConponentTest1へDictionalyオブジェクトを渡す例です。
Dictionary<string, object> _formAttributeのkeyに"KV"を指定することで、
ConponentTest1の変数KVにTestPageのOnInitializedAsync()で格納したDictionary値が、属性スプラッティングによって格納されます。
KVsumも然り。

TestPage

TestPage.razor
@*TestPage.razor*@
@page "/mytest"
<PageTitle>MyTest</PageTitle>
<span>
    <ComponentTest1 @attributes = "@_formAttribute" />
</span>
TestPage.razor.cs
// TestPage.razor.cs
public partial class TestPage
{
    private readonly Dictionary<string, object> _formAttribute = [];

    protected override async Task OnInitializedAsync()
    {
        // ComponentTest1のMsgというパラメータ属性のついた変数にTest Messageという文字列を渡す用。
        _formAttribute.Add("Msg", "Test Message");
        // ComponentTest1のKVというパラメータ属性のついた変数にDictionaryオブジェクトを渡す用。
        _formAttribute.Add("KV", new Dictionary<string, object>
        {
            { "Key1", "TestValue1" },
            { "Key2", "TestValue2" },
            { "Key3", "TestValue3" },
            { "Key4", "TestValue4" }
        });
        // ComponentTest1のKVsumというパラメータ属性のついた変数にDictionaryオブジェクトを渡す用。
        _formAttribute.Add("KVsum", new Dictionary<string, object>
        {
            { "Key1", 3 },
            { "Key2", 6 },
            { "Key3", 9 }
        });
        await base.OnInitializedAsync();
    }
}

ComponentTest1

ComponentTest1.razor
@*ComponentTest1.razor*@
<h3>ComponentTest1</h3>

@*メッセージの変数を表示*@
<span>@Msg</span>

@*KeyValueを列挙表示*@
<ul>
   @foreach (var o in KV)
   {
       <li>@o.Key: @o.Value</li>
   }
</ul>

@*sumValueを表示*@
<span>@sumValue</span>
ComponentTest1.razor.cs
// ComponentTest1.razor.cs
public partial class ComponentTest1
{
    [Parameter]
    public string? Msg { get; set; }
    [Parameter]
    public Dictionary<string, object> KV { get; set; } = [];
    [Parameter]
    public Dictionary<string, object> KVsum { get; set; } = [];
    private int sumValue = 0;

    protected override async Task OnInitializedAsync()
    {
        foreach (KeyValuePair<string, object> item in KVsum)
        {
            if (item.Value is int value)
            {
                sumValue += value;
            }
        }
        await base.OnInitializedAsync();
    }
}

実行結果:
KVとKVsumにTestPageで格納したDictionalyオブジェクトが属性スプラッティングで格納され、TestPage上のComponentTest1に表示されました。

利用ケース

私の設計では、属性スプラッティングattributeに渡すkey-valueをDBやXMLに外に出しておいて、後からビルドせずで変更が可能な方針することが多いです。

まとめ

Blazorでの属性スプラッティングの動きを確認しました。
属性スプラッティングは使う場面が多い認識なので、何かのお役に立てれば幸いです。

参考

  • Microsoft Blazor とは

https://dotnet.microsoft.com/ja-jp/apps/aspnet/web-apps/blazor

  • Microsoft Learn ASP.NET Core Blazor 属性スプラッティングと任意のパラメーター

https://learn.microsoft.com/ja-jp/aspnet/core/blazor/components/splat-attributes-and-arbitrary-parameters?view=aspnetcore-9.0

  • 【Blazor】属性スプラッティングで任意のパラメーターを設定する方法

https://blazor-master.com/blazor-attribute-splatting/

Discussion