Blazor アプリで、bool スイッチ値に基づいて CSS クラス文字列を動的に構築する

# Angular アプリでの CSS クラス文字列の構築

Angular SPA プログラムをコーディングしているとき、このように CSS クラス文字列を制御するコードを書くことができます。

```ts
// my.component.ts
@Component({template:'my.component.html',...})
export class MyComponent {
  rotate: boolean;
  disabled: boolean;
  ...
```

```html
<!-- my.component.html -->
<div [ngClass]="{'rotate':rotate, 'disabled':disabled}">...
```

上記のコードは、コンポーネントの `rotate` および `disabled` プロパティが true か否かに応じて、このようなDOMをレンダリングします。

```html
<div class="rotate disabled">...
```

この Angular の `ngClass` ディレクティブは直感的でわかりやすいと思います。

# Blazor だと、ちょっとつらい...

ところで、Blazor C＃SPA プログラムをコーディングしているとき、CSS クラス文字列の制御は少し苦痛です。

Blazor においては、Angular の方法のようにではなく、自分で条件を判断して CSS クラス文字列を連結するコードを作成する必要がありました。

```csharp
@* MyComponent.razor *@
<div class="@CssClass()">...

@code {
  bool Rotate;
  bool Disabled;
  
  // CSS クラス文字列の構築が必要なときに毎回、こんなコードを書かなきゃいけないの!?
  string CssClass() {
    var cssClass = new List<string>();
    if (this.Rotate) cssClass.Add("rotate");
    if (this.Disabled) cssClass.Add("disabled");
    return string.Join(' ', cssClass);
  }
  ...
```

自分はこのようなやり方では満足できませんでした。

さてどうしたものでしょうか。

# 基本コンセプト

ということで、、Angular の方法のように、匿名型オブジェクトから CSS クラス文字列を構築するユーティリティ関数を自分で作成することにしました。

ユーティリティ関数の基本コンセプトは次のとおりです。

1. この関数には、匿名オブジェクトを渡すことができます。
2. この関数は .NET CLR の「リフレクション」機能を使い、引数のオブジェクトの `bool` 型のプロパティを取得します。
3. この関数は、そのプロパティの値が `true` であるプロパティに絞り込みます。
4. 最後に、関数はこれらのプロパティ名とスペース区切り文字を連結した文字列を返します。

この基本コンセプトをクラスライブラリに実装しました。

クラス名は `CssClassInlineBuilder` とし、静的メソッド `CssClass()` として実装しました。

`CssClass()` 静的メソッドのソースコードは次のとおりです。

```csharp
// CssClassInlineBuilder.cs
public static class CssClassInlineBuilder 
{
  public static string CssClass(object obj)
  {
    var boolPropNames = obj.GetType()
      // Enumarate all properties of the argument object.
      .GetProperties()
      // Filter the properties only it's type is "bool".
      .Where(p => p.PropertyType == typeof(bool))
      // Filter the properties only that value is "true".
      .Where(p => (bool)p.GetMethod().Invoke(obj, null))
      // Collect names of properties, and convert it to lower case.
      .Selec(p => p.Name.ToLower());

    // Concatenate names of filtered properties with space separator.
    return string.Join(' ', boolPropNames);
  }
}
```

このクラスライブラリを使用すると、Angular の方法のように、コードスニペットなしで CSS クラス文字列を動的に構築するコードを記述できます!

```csharp
@* MyComponent.razor *@
<div class="@CssClassInlineBuilder.CssClass(new {Rotate, Disabled})">...

@code {
  bool Rotate;
  bool Disabled;
  ...
```

いい感じですね ;)

...しかしながら、自分はまだ、これでも満足できませんでした。

`CssClassInlineBuilder.CssClass(new {...})` というコード、**あまりに長すぎませんかね?**

# もっとシンプルに!

C＃構文の1つが役に立ちました！

クラス名を省略するために `using static` ディレクティブを使用したのです。

```csharp
@* _Imports.razor *@
...
@using static CssClassInlineBuilder
```

最終的に、メソッド名だけで記述することができるようになりました!

```csharp
@* MyComponent.razor *@
<div class="@CssClass(new {Rotate, Disabled})">...

@code {
  bool Rotate;
  bool Disabled;
  ...
```

これで寿司も回せますね! :)
(Source code is on [GitHub](https://github.com/jsakamoto/CssClassInlineBuilder/blob/master/SampleSite/SushiGoRound/App.razor), inspired by ["sushi-go-round"](https://github.com/EndoHizumi/sushi-go-round).)

![fig2](https://raw.githubusercontent.com/jsakamoto/CssClassInlineBuilder/master/assets/movie1.gif)

# まとめ

この実装をブラッシュアップし、このクラスライブラリを NuGet パッケージとして公開しました。

このライブラリに興味があれば、以下のリンクをたどってください。

- [**"CSS Class Inline Builder"**](https://www.nuget.org/packages/Toolbelt.Web.CssClassInlineBuilder/) [![NuGet Package](https://img.shields.io/nuget/v/Toolbelt.Web.CssClassInlineBuilder.svg)](https://www.nuget.org/packages/Toolbelt.Web.CssClassInlineBuilder/)
![fig1](https://raw.githubusercontent.com/jsakamoto/CssClassInlineBuilder/master/assets/fig1.png)

すべてのソースコードは GitHub 上にあります。

- [https://github.com/jsakamoto/CssClassInlineBuilder](https://github.com/jsakamoto/CssClassInlineBuilder)

----

Blazor で Happy coding :)