Blazor:オリジナルのInputコンポーネントを作成する

C#

Blazorには、「InputText」や「InputNumber」など、フォーム用の様々なコンポーネントが用意されています。さらに、「InputBase」を継承することでオリジナルのコンポーネントを作成することもできます。

表示のフォーマットを変更したり、ラベルやエラーメッセージをまとめて1つのコンポーネントにすることでコード量を削減することもできます。今回は、オリジナルのInputコンポーネントを作成して、様々な機能を試してみます。

前提

以下の環境で実行しています。

oswindows11
dotnet8.0.304
UIBlazor(WebAssembly standalone)
IDEVisual Studio 2022 Community

本記事に記載しているソースコードは説明に必要な個所のみ抜粋していること、ご了承ください。

Blazorプロジェクトは作成済みであることを前提としています。

数値のフォーマット変更

フォームに入力した数字を3桁区切りにするなど、表示形式を変えたい場合があると思います。「InputText」や「InputNumber」では指定できないようでしたが、オリジナルのコンポーネントを作成することで、自由に設定できます。

InputBaseを継承したコンポーネントを作成

以下が数値を3桁区切りで表示するための最低限のコードになります。

@* InputCustom *@

@inherits InputBase<double>

<div class="form-group mt-2">
	<input class="form-control" @bind="CurrentValueAsString" />
</div>

@code {
	[Parameter]
	public string Format { get; set; } = "N0";

	protected override string FormatValueAsString(double value) => value.ToString(Format);

	protected override bool TryParseValueFromString(string? value, [MaybeNullWhen(false)] out double result, [NotNullWhen(false)] out string validationErrorMessage)
	{
		if (double.TryParse(value, out result))
		{
			validationErrorMessage = string.Empty;
			return true;
		}
		else
		{
			result = 0;
			validationErrorMessage = $"数値を入力してください";
			return false;
		}
	}
}

まずは3行目のように「InputBase」を継承します。型パラメータではdoubleを指定していますが、ここは目的に沿う型に置き換えて指定してください。

また、11行目で、パラメータとしてフォーマットの指定を受け取ります。

次に2つのメソッドをoverrideします。「FormatValueAsString」メソッドは表示形式を指定するメソッドです。このメソッドの戻り値としてフォーマット変換を施した文字列を返却します。返却された表示用の文字列は親コンポーネントの「CurrentValueAsString」プロパティに設定されていますので、このプロパティをテキストボックス(input)にバインドさせます。

TryParseValueFromString」はHTMLのinput要素から受け取った文字列を対象の型(今回はdouble)に変換するメソッドです。変換できない場合は、「validationErrorMessage」にエラーメッセージを設定します。

今の時点では、「validationErrorMessage」にエラーメッセージを設定しても画面に表示されません。エラーメッセージの表示の追加は後程行います。

このコンポーネントは以下のように使用します。

<EditForm Model="Model" OnValidSubmit="Check">
    <InputCustom @bind-Value="Model.Money" />
    <div class="mt-2">@Model.Money</div>
    <button class="btn btn-primary mt-3">送信</button>
</EditForm>

実行すると以下のようにテキストボックスが表示されます。ここに数値を入れると3桁区切りになります。参考のためテキストボックスの下にテキストボックスとバインドしているMoneyプロパティの値を表示しています。

エラーメッセージ処理の追加

次に、文字列など数値以外のデータが入力され変換できなかった際に、エラーを表示させる処理を追加します。

Validationコンポーネントの追加

以下のように、「InputCustom」コンポーネントの4行目と9行目にエラーメッセージ用の処理を追加します。「ValidationMessage」コンポーネントを使用することで、「validationErrorMessage」に設定したエラーメッセージを表示することができます。

@* InputCustom *@
<div class="form-group mt-2">
	<input class="form-control" @bind="CurrentValueAsString" />
	<ValidationMessage For="@ValidationError" />
</div>

@code {
	[Parameter, EditorRequired]
	public Expression<Func<double>> ValidationError { get; set; } = default!;
}

InputCustom」コンポーネントの呼び出し方も以下のように変更します。Blazorで一般的にFormにValidationを追加するのと同じやり方です。

<InputCustom @bind-Value="Model.Money" ValidationError="@(()=>Model.Money)" />

では、適当に文字列を入れてみましょう。

エラーメッセージが表示されました。しかし、気になる箇所が1点あります。一般的なValidationではエラーが発生したコンポーネント(テキストボックス)が赤く縁取りされますが、それが今回はありません。

Cssの追加

今回のようにテキストボックスが1つしかなければ問題ありませんが、複数ある場合にどこがダメなのか視覚的に分かるようにするため、赤い縁取りを追加します。方法は簡単で、inputのclass属性に「CssClass」を指定するだけです。「CssClass」は親コンポーネントが持っているプロパティです。

@* InputCustom *@
<div class="form-group mt-2">
	<label for="@For" class="form-label">@FieldName</label>
	<input @attributes="AdditionalAttributes" class="form-control @CssClass"
		   @bind="CurrentValueAsString" />
	<ValidationMessage For="@ValidationError" />
</div>
<InputCustom @bind-Value="Model.Money" ValidationError="@(()=>Model.Money)"
					 FieldName="お金" id="money" />

問題なく赤枠が表示されています。また、Validationが成功(数字を入力)すれば、枠の色は緑色に変わります。

今回の本題とはそれますが、InputCustomコンポーネントにラベル(<label>)を追加しています。そしてInputCustomコンポーネント呼び出すときに、id属性を付与しています。

inputに「@attributes=”AdditionalAttributes”」を付与していますが、この「AdditionalAttributes」も親コンポーネントのプロパティです。辞書型でり、呼び出すときに指定した「id=”money”」が格納されています。

labelにはfor属性を付与しています。これは以下のように「AdditionalAttributes」からidの値を取り出して設定しています。labelfor属性にinputid属性と同じ値を設定することで、ラベルをクリックしたときにinput(テキストボックス)にフォーカスが当たるようになります。

For = AdditionalAttributes?.Where(k => k.Key.Equals("id"))
							.Select(v => v.Value)
							.FirstOrDefault()?.ToString();

コメント