Blazor01

BlazorでのWebアプリケーション開発手法1【C#】B19_OnParametersSet

前回のレクチャーでは、[Parameter]属性を付けたプロパティに初期値を書いても意味がないという注意点を確認しました。Razorコンポーネントが生成される際には一度その初期値で初期化されるものの、その後URL経由でパラメータが渡されると、指定がない場合でも型の既定値(floatなら0、DateTimeならMinValue、stringならnull)で上書きされてしまうためです。今回は、この性質を踏まえたうえで「パラメータが指定されていなければハイフン表示、指定されていれば指定された値を表示する」という、これまでの計測画面が本来やりたかった挙動をどう実装するかを解説します。

開始時点のMeasurePage.razorでは、下図のように各[Parameter]プロパティに初期値(MeasureValueに999f、MeasureDateにDateTime.MaxValue、MeasureAreaに”しょきち”)が書かれたままになっています。

図1 初期値が書かれたままの[Parameter]プロパティ

この初期値はどのみち上書きされてしまうため意味がありません。そこでまず、3つのプロパティから初期値の記述を削除します。

[Parameter]
public float MeasureValue { get; set; }
[Parameter]
public DateTime MeasureDate { get; set; }

 

図2 初期値を削除した状態の[Parameter]プロパティ

基本的な方針として、[Parameter]プロパティはあくまでURLからパラメータを受け取るためだけに使い、画面(HTML側)へのデータバインドは専用の変数(これまで通り_measureValue、_measureDate、_measureArea)に対して行うようにします。そこでHTML側のバインド先を、パラメータのプロパティから元の_measureValueなどのフィールドに戻します。

図3 HTML側のバインド先を_measureValueなどのフィールドに戻したところ

この状態でパラメータなしで実行すると、_measureValueなど各フィールドに設定した既定値(”–“や”神戸”)がそのまま表示されます。一方、URLでパラメータを3つとも指定して実行しても、パラメータの値をフィールドに代入する処理がどこにもないため、結果は同じデフォルト値のままになってしまいます。

図4 URLで3つのパラメータを指定しても表示はデフォルト値のまま

そこで、パラメータで受け取った値を_measureValueなどの変数に代入したいのですが、問題はそれをどこで行うかです。Windows Formsであればコンストラクタで初期化するのが定石ですが、Blazorのコンポーネントで同じようにコンストラクタを使うとどうなるかを確認します。

public MeasurePage()
{
    _measureValue = MeasureValue.ToString();
}

 

図5 コンストラクタでMeasureValueをフィールドに代入しようとしたコード

コンストラクタの1行目にブレークポイントを設定してデバッグ実行すると、URLで3つのパラメータを渡しているにもかかわらず、この時点ではMeasureValueは0、MeasureDateはMinValue、MeasureAreaはnullと、すべて型の既定値のままであることが分かります。

図6 コンストラクタの時点ではパラメータがまだ既定値のまま(MeasureAreaはnull)

つまりこのコンストラクタは、パラメータの値が設定される前に呼び出されています。Razorコンポーネントが作られる際には、まずコンストラクタが実行され、その後でURLパラメータの設定処理が動く、という順序になっているため、コンストラクタの中ではパラメータの値を利用することができません。

では、どこで設定すればよいのか。@codeブロック内で「protected override」と入力し、続けて「On」と入力すると、IntelliSenseの候補にOnParametersSetというメソッドが表示されます。これは、URL指定でパラメータがセットされたあとに呼ばれるように設計されているメソッドです。先ほどのコンストラクタは削除し(中身は空にし)、代わりにこのOnParametersSetをオーバーライドします。

protected override void OnParametersSet()
{
}

 

図7 OnParametersSetをオーバーライドした直後(中身は空)

この中でパラメータの値をフィールドに代入していきます。ここで、単純に「MeasureValueの値が0なら未指定」と判定してしまうと、本当に0という値が渡された場合と区別がつかなくなってしまいます。そこで、MeasureValueやMeasureDateの型に?を付けてfloat?・DateTime?というnull許容型に変更し、HasValueで「値が設定されているかどうか」を判定します。HasValueがtrueであれば、値がある(=パラメータが指定された)ということです。

[Parameter]
public float? MeasureValue { get; set; }
[Parameter]
public DateTime? MeasureDate { get; set; }
[Parameter]
public string MeasureArea { get; set; }
 
protected override void OnParametersSet()
{
    if (MeasureValue.HasValue)
    {
    }
}

 

図8 float?に変更し、HasValueで判定するif文を追加したところ

if文の中では、計測値を取得する処理(B07で実装したMeasureServiceの呼び出し)と同じ編集で、Math.Roundで小数点以下2桁に丸めたうえで単位” m/s”を付与し、_measureValueに代入します。null許容型の実際の値を取り出すには.Valueを使います。?を付けているためMeasureValue自体はfloat?型ですが、.Valueとすることでfloat型の値として扱えます。

if (MeasureValue.HasValue)
{
    _measureValue = Math.Round(MeasureValue.Value, 2) + " m/s";
}

 

図9 MeasureValue.Valueを使って_measureValueに代入する処理

同じ要領で、MeasureDateについてもHasValueで判定し、Valueで取り出したDateTime型の値を、計測時と同じ書式(”yyyy/MM/dd HH:mm:ss”)でToStringし_measureDateに代入します。MeasureAreaはstring型なのでHasValueではなく、nullでなければという条件(!= null)で判定し、そのまま_measureAreaに代入します。

if (MeasureDate.HasValue)
{
    _measureDate = MeasureDate.Value.ToString("yyyy/MM/dd HH:mm:ss");
}
 
if (MeasureArea != null)
{
    _measureArea = MeasureArea;
}

 

図10 MeasureDate・MeasureAreaも含めた3ブロックの実装

OnParametersSetメソッドの完成形は以下の通りです。パラメータが指定されていればその値を、指定されていなければ元のフィールドの既定値(”–“や”神戸”)をそのまま使う、という当初の狙い通りの動作になります。

protected override void OnParametersSet()
{
    if (MeasureValue.HasValue)
    {
        _measureValue = Math.Round(MeasureValue.Value, 2) + " m/s";
    }
 
    if (MeasureDate.HasValue)
    {
        _measureDate = MeasureDate.Value.ToString("yyyy/MM/dd HH:mm:ss");
    }
 
    if (MeasureArea != null)
    {
        _measureArea = MeasureArea;
    }
}

 

実行して動作を確認します。Home画面には、これまでのレクチャーで作成した「引数なし」「引数1個」「引数2個」「引数3個」のリンクとボタンが並んでいます。

図11 Home画面。パラメータなしで計測画面を開くと計測値・計測日時は–、計測場所は神戸のまま

パラメータなしで計測画面を開くと、計測値・計測日時は”–“、計測場所は”神戸”という、フィールドに設定した既定値がそのまま表示されます。一方、「引数3個」のリンク(計測値・計測日時・計測場所をURLで指定)から開くと、今度はOnParametersSetで代入されたパラメータの値(12.8 m/s、2024/12/04 12:06:53、立川)が正しく表示されます。

図12 3つのパラメータを指定して開くと、指定した値(12.8 m/s/2024/12/04 12:06:53/立川)が表示される

パラメータが指定されなければ既定値の”–“のまま、指定されればパラメータの値が入る、という動作になっているのは、OnParametersSetの中でパラメータがセットされていた場合にだけその値をフィールドに反映する処理を書いているためです。この条件を通らなければ、そもそものフィールドの既定値のままになります。

このように、パラメータの値を使って何らかのロジックを書きたい場合はOnParametersSetを使います。コンストラクタは使えません。

なお、OnParametersSetには非同期版も用意されています。@codeブロックで「protected override」に続けて「On」と入力すると、OnParametersSetAsyncという候補も表示されるので、これを使えば非同期で処理を書くことができます。

protected override Task OnParametersSetAsync()
{
    return base.OnParametersSetAsync();
}

 

図13 IntelliSenseから自動生成したOnParametersSetAsyncの雛形

パラメータを受けて、検索など時間のかかる処理を行うのであれば非同期版を使うとよいですが、今回のように値をセットするだけの一瞬で終わる処理であれば、わざわざ非同期にする必要はありません。非同期にすると、非同期特有のテストや考慮事項が新たに発生してしまうため、1秒・2秒といった時間がかかることが見込まれる処理でなければ、同期のOnParametersSetのままにしておくほうがよいでしょう。

BlazorでのWebアプリケーション開発手法1【C#】

A01_はじめに
A02_バージョンの確認
A03_プロジェクトの作成
A04_アプリの実行
A05_サンプルプログラムの構成
A06_Razorコンポーネント
A07_Razor構文
A08_@pageでURLを指定する
A09_@rendermode
A10_PageTitle
A11_HTMLの見出し
A12_pタグ
A13_単方向データバインド
A14_単方向データバインドの詳細
A15_Bootstrap
A16_ボタンクリックイベント
A17_イベント引数の受け取り方
A18_ここまでのまとめ
B01_新しいWebページを追加する方法
B02_サイドバーから画面を表示する
B03_サイドバーのアイコンを変更する方法
B04_計測値を表示する
B05_変数に置き換える
B06_最新の値でデータを更新する
B07_@usingで通常のC#コードを呼び出す
B08_画面遷移_aタグ
B09_画面遷移_aタグの見た目をボタンにする
B10_画面遷移_NavigationManagerでの遷移
B11_画面間のパラメータの受け渡し方法
B12_EscapeDataStringでエスケープする
B13_複数のパラメータを渡す
B14_任意のパラメータにする
B15_string以外の型をパラメータで使う方法
B16_DateTime型をパラメータで渡す方法
B17_引数2個のURLも型指定に変更する
B18_パラメータ属性をつけるときの注意点
B19_OnParametersSet
B20_OnInitialized
B21_OnInitializedが2回通る問題の対応
B22_ブラウザが開いている間の値を記憶する方法
B23_値の受け渡しとしてのProtectedSessionStorage
B24_非同期を想定したUIの制御
C01_一覧表示
C02_非同期での一覧表示
C03_table-striped
D01_入力エリア
D02_双方向データバインド
D03_入力データをリアルタイムで通知する方法
D04_placeholder
D05_入力エリアの横幅の調整
D06_テキストチェンジイベントを拾う方法
D07_プルダウン
D08_プルダウンのbootstrap
D09_プルダウンの選択している値をデータバインドで操作する
D10_プルダウンにリストの値を表示する
D11_プルダウンに未選択行を入れる
D12_プルダウンの入力を禁止する
D13_プルダウンのチェンジイベントを取得する
D14_チェックボックス
D15_チェックボックスにラベルを付ける
D16_複数のチェックボックスを並べる
D17_ラジオボタン
D18_ラジオボタンのデータバインド
D19_ラジオボタンのチェンジイベント
D20_ラジオボタンにbootstrapを適応する