Blazor で データバインディングと変更時イベントを同時に処理する悩みどころ

Blazor で データバインディングと変更時イベントを同時に処理する悩みどころ

実現したいこと

例えば、検索キーワードを入力中に選択候補を絞り込む仕様があるとします。

その仕様を実現しようとした場合、

  • 検索キーワードをバインドして @bind-value="SearchValue"
  • 入力時に値を反映したいのでバインドのタイミングは @bind-value:event="oninput"
  • フィルタリングの処理は、入力イベントで処理したいため、 @oninput="OnFiltering"
<input  @bind-value="SearchValue" @bind-value:event="oninput" @oninput="OnFiltering" />

但し、このコードはエラーが発生します。

The attribute 'oninput' is used two or more times for this element. Attributes must be unique (case-insensitive). The attribute 'oninput' is used by the '@bind-value' directive attribute

実はバインド構文は、Blazor フレームワーク側でイベントに置き換えて動作しているため"oninput" が二回定義されているよ。と怒られてしまっているのです。

詳細は下記ページ参照 ASP.NET Core Blazor データ バインディング | Microsoft Docs

回避策

回避策その1 - イベント内部で自前で バインドプロパティに値を設定する。

<input  @bind-value="SearchValue" @oninput="OnFiltering" />
private void OnFiltering(ChangeEventArgs changeEventArgs)
{
    // 入力した値を即時反映する
    this.SearchValue = changeEventArgs.Value as string;
    // フィルター処理を行う
    Filtering();
}

この方法の個人的によくないポイントは、ChangeEventArgs を @code で受け取らないといけないという点ですね。 コード側(c#) にイベント引数が渡ると、画面情報を生で渡してしまいスパゲッティコードになる危険性があります。

回避策その1 - バインドしたプロパティの set に処理を書く。

<input  @bind-value="SearchValue" @bind-value:event="oninput"  />
public string SearchValue
{
    get
    {
        return _SearchValue;
    }
    set
    {
        _SearchValue = value;
        // フィルター処理を行う
        Filtering();
    }
}

この方法は、 setter が単純にデータを保持するプロパティの役割以外に、フィルター処理と連動しているため、保守性が悪くなる可能性があります。