ユーザ用ツール

サイト用ツール


it技術:web開発:blzaor

Blazor

便利ツール

Tips

初回アクセスページ

Blazor Server

Pages フォルダにある「_Host.cshtml」ファイルとなる。

Blazor WebAssembly

wwwroot フォルダにある「index.html」ファイルとなる。

UIフレームワーク

MatBlazor が一番有名な UI フレームワークなのですが、GitHub のスターがより多い「Ant Design Blazor」となる。
【無料】Blazor 対応の「使える」UI フレームワーク5選

https://ant.design/components/overview/

AntDesign 採用例

BlazorStrap 採用例

Blazor Server Side へのインストール方法
  1. nuget から BlazorStrap と BlazorStrap.V5 の2つのパッケージをダウンロードします。
  2. _host.cshtml を次のように変更します。
    1. <head>追加されていることを確認 (YourAssemblyName)の部分は書き換える
      1. <link rel=“stylesheet” href=“css/bootstrap/bootstrap.min.css” />
      2. <link href=“(YourAssemblyName).styles.css” rel=“stylesheet”>
    2. <body>追加 の最後に
    3. <script src=“_content/BlazorStrap/popper.min.js”></script>
    4. <script src=“_content/BlazorStrap/blazorstrap.js”></script>
      V5.2.100-Preview3 でblazorstrap.jsを削除
  3. _Program.cs に追加
    1. using BlazorStrap;
    2. builder.Services.AddBlazorStrap();
  4. _Imports.razor に追加
    1. @using BlazorStrap.V5
  5. MainLayout.razor の class=“page”の最後に下記を追加
    1. <BSCore />

NuGet参照したフレームワークの場所「_content」

NuGet参照したUIフレームワークの「BlazorStrap」など

以下の命名則の URL で、これらの静的 Web アセットを参照できる。

  1. “_content/“で始まり、
  2. 続けて、パッケージの名前、
  3. 最後に、アセットファイルのファイル名
<script src="_content/BlazorStrap/popper.min.js"></script>
<script src="_content/BlazorStrap/blazorStrap.js"></script>

NuGetのパッケージキャッシュフォルダは、デフォルトは「%userprofile%\.nuget\packages」

スタートアップページの設定

Blazor アプリケーションのスタートアップ ページは Index.razor です。
これを Login.razor に変更したい場合、下記のようにします。

  1. Index.razorから「@page ”/“」 を削除します。
  2. Login.razor に「@page ”/“」 を追加します。

Setting a startup page in Blazor - stackoverflow

アイコン使用

Bootstrap Icons

1,800以上のアイコンが使用できる。
https://icons.getbootstrap.com/

本家からはnpmのみで、NuGetによるインストールは用意されていない。

Using Bootstrap Icons in ASP.NET Core Blazor project

任意のサイズ

font-size と color を使用して、アイコンの外観を変更
https://bootstrap-guide.com/extend/icons/bootstrap-icons

<i class="bi bi-alarm" style="font-size: 2rem; color: cornflowerblue;"></i>

FontAwesome

Web系の開発でアイコン表示などによく使われている。

現時点で、Font Awesome 6が最新である。

Open Iconic

.NET 7までは、「Open Iconic v1.1.1」のアイコン223種類が使用できる。.NET 8から除外された。
https://www.nsbasic.com/app/OpenIconic.html

Open Iconicは223ものアイコンをOSSとして公開されているアイコンフォントです。EOTやWOFF、OTFだけでなく、PNG、SVGなどのフォーマットも用意されています。また、マルチカラー化や用意された複数サイズのフォントを利用してRWDに対応する、等も可能のようです。シンプルで汎用性の高そうなデザインですね。ライセンスはMIT、フォントはOFLとして公開されています。
オープンソースとして公開されているアイコンフォントのセット・「Open Iconic」

ナビゲーションのホームアイコン

<span class="oi oi-home"></span>

Razorコンポーネントのライフサイクル

【Blazor】Razorコンポーネントのライフサイクルを解説する

実行の順番

メソッド名呼び出し補足
SetParametersAsyncパラメーターが設定されるタイミング
OnInitialized(Async)コンポーネントが初期化されるタイミング
OnParametersSet(Async)コンポーネントが初期化されるタイミングと、受け取るパラメータが更新されたタイミング
OnAfterRender(Async)コンポーネントがレンダリングされたあと初回実行のときは、引数の firstRender が true となる
ShouldRenderコンポーネントがレンダリングされるたびtrue を返すとレンダリングを続行する
StateHasChangedレンダリングしたい任意のタイミングコストが高いので不必要に呼び出さないこと

OnParametersSet(Async)について

Navigation.NavigateToで別ページに画面遷移させた時でもOnParametersSetAsyncが動作する。

Navigation.NavigateToで別ページに画面遷移させた時に例外エラー、原因として OnParametersSetAsync処理内で発生していた。 NavigateTo前に_isNextPageフラグをTrueにセットして使用して回避させた。

protected override async Task OnParametersSetAsync()
{
     // 別ページに画面遷移する場合は何もしない。
     if (_isNextPage) return;

Razorコンポーネントのステート

Blazor のコンポーネントのステートについて

ステートの変更通知はどのようなタイミングで発生するのかですが、おおまかに下記の3パターンで発生します。

  1. StateHasChanged メソッドを明示的に呼び出したとき
  2. イベント発火時 (@bind や @onclick とか)
  3. Parameter が変わったとき

2つ目と3つ目は暗黙的に StateHasChanged を呼び出している。
StateHasChanged はステートが変更されたことを通知するメソッドです。このメソッドを呼び出すと再レンダリング候補としてマークされます。

コードビハインド

razorファイルの中にHTML要素とバインドするC#コードを合わせた記載はやめて、razorコンポーネントとC#コードを分離して記載する。
Blazorでコードビハインドでロジックとビューを分離して記述する

ロジックとビューを分離することで、下記のようなメリットなどが得られます。

  • ロジックに対するUnitTestの記載が可能
  • 基底のコンポーネントクラスの作成などによる共通処理の作成

方法

raozrファイルと対になるようにcsのファイルを作成するだけです。

Counter.razor Counter.razor.cs(追加)

コード側のクラスには、partialキーワードを付けます。
例 public partial class Counter

最大長(maxlength)を設定する方法

BlazorのInputコンポーネントに maxLength が見つからない。

これらの組み込みコンポーネントは、レンダリングされた出力に HTML 属性を渡します。したがって、例で行ったのとまったく同じように、HTML 属性を指定するだけでかまいません
How to set textarea/input max length in Blazor - stackoverflow

組み込み専用の属性がないだけで、HTML 属性はそのまま指定すれば機能する。

<BSInput InputType="InputType.Text" placeholder="工程コード" Value="@("")" maxlength="5"  />

ボタンにフォーカスを設定する方法

UIフレームワーク未使用

有名なUIフレームワークでも、Buttonのフォーカスが用意されていないことがあるので、その場合に標準機能で代替する。

<button class="btn btn-primary" @onclick="IncrementCount" @ref="ControlButton">Click me</button>
@code {
    public ElementReference? ControlButton { get; set; } = default!;
 
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (ControlButton != null)
            await ControlButton.Value.FocusAsync();
     }
}

BlazorStrap使用

※BlazorStrap/5.2.100-Preview3b が必要
https://github.com/chanan/BlazorStrap/issues/599

<BSButton Color="BSColor.Primary"@onclick="IncrementCount" @ref="ControlButton">Click me</BSButton>
@code {
    public ElementReference? ControlButton { get; set; } = default!;
 
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (ControlButton != null && ControlButton.Element != null)
            await ControlButton.Element.Value.FocusAsync();
     }
}

BSInputGroupを中央寄せにする方法

BSInputGroupでdisplay:flex となっているため、style属性で”display:block に書き換えてから中央寄せ(text-align: center)とする。

<BSInputGroup MarginBottom="Margins.Medium" style="display:block; text-align: center">
    <div Class="@BS.Form_Check_Inline">
        <BSInputRadio CheckedValue="@("on")" @bind-Value="_value" @onchange="OnChange" />
        <BSLabel IsCheckLabel="true">バーコード</BSLabel>
    </div>
    <div Class="@BS.Form_Check_Inline">
        <BSInputRadio CheckedValue="@("off")" @bind-Value="_value" @onchange="OnChange" />
        <BSLabel IsCheckLabel="true">手動選択</BSLabel>
    </div>
</BSInputGroup>

Boostrap 5による変更点

bootstrap5では中東の言語で主流である右書きに対応したことからleft→start、right→endという概念の変更が導入されました。
これにより、ml-〇 → ms-〇にmr-〇 → me-〇 というようにクラス名が変更になりました。

【bootstrap5】classのml、plとmr、prの名称が変更

HTMLタグを出力する

MarkupStringオブジェクトにキャストすることでタグの内容を出力できます。

<div>
  <button @onclick="ButtonClick">Button1</button>
  <p>@((MarkupString)messageText)</p>
</div>
@code {
  string messageText;
 
  void ButtonClick()
  {
    messageText = "<h3>見出しです</h3><p>My Button Clicked</p>";
  }
}

最上位のCSSを定義

フォントサイズなど、bootstrap.css で定義されたサイズが優先されてしまう場合、important.css にてCSSを定義することにより優先させるようにする。

_Host.cshtml
    <link rel="stylesheet" href="css/important.css" />
    <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />

画面サイズによりフォントサイズを変更する。

important.css
.font-adjust {
    font-size: 16px !important;
}
 
    @media (min-width: 760px) {
        .font-adjust {
            font-size: calc(16px + ((1vw - 7.6px)*(16/4.4))) !important;
        }
    }
 
    @media (min-width: 1200px) {
        .font-adjust {
            font-size: 32px !important;
        }
    }

テキストボックスのEnterキーでサブミットさせる

IsSubmit=“true” を付けることで、Enterキーでサブミットさせることが出来る。

<BSInput InputType="InputType.Text" IsSubmit="true"  @bind-Value="Model.Input1" class="font-adjust" />

bindとonchangeの併用は不可

onchange を追加すると、Model.Procedure に値がセットされなくなる。

<BSInput InputType="InputType.Select" @bind-Value="Model.Procedure" @ref="ProcedureControl" @onchange="OnProcejureChange">

bindとonchangeの併用は不可

対応

onchange イベント処理内で値をセットする。

private void OnProcejureChange(ChangeEventArgs e)
{
    Model.Procedure = e.Value.ToString() ?? "";
}

MainLayout コンポーネント側にデータを反映させる

カスケーディングを使用してパラメーターを渡す。

カスケードとは、もともと「連なった小さな滝」という意味があります。
Blazor では滝が流れるイメージで、上位の階層から下位の階層にパラメーターを渡すことができる仕組みがあります。
これが「カスケーディングパラメーター」です。

クライアントのIPアドレスの取得

BlazorServerではサーバーで動作しているので、クライアントのIPアドレスを取得するにはクライアントから情報を取得する。
MainLayoutの子コンポーネント側に表示させるため、カスケーディングを使用してパラメーターを渡す方式にした。

BlazorServerでクライアントのIPアドレスを全ページで共有する

クエリパラメーターのセットと取得

単独変数

var parameter = new Dictionary<string, object?>
{
    { "CPCode", value },
    { "TermID", Layout.Info.IPAddress }
};
var newuri = Navigation.GetUriWithQueryParameters("./Machine", parameter);
Navigation.NavigateTo(newuri, false);
[Parameter, SupplyParameterFromQuery]
public string? CPCode { get; set; }
 
[Parameter, SupplyParameterFromQuery]
public string? TermID { get; set; }

複数変数

var readOnlyDictionary = new Dictionary<string, object>
{
    ["film"] = 1,
    ["film"] = 2,
};
 
var uri = Navigation.GetUriWithQueryParameters(readOnlyDictionary);
// http://localhost:5000/MovieComparer?film=1&film=2&film=3
[Parameter, SupplyParameterFromQuery(Name = "film")]  
public string[]? Films { get; set; }

参照

セッションの扱い(状態管理)

コンポーネント内でデータを保存し、読み込む

ブラウザー ストレージのデータの読み込みまたは保存が必要なすべてのコンポーネントで、@inject ディレクティブを使用して、次のいずれかのインスタンスを挿入します。

  • ProtectedSessionStorage
  • ProtectedLocalStorage

BlazorでWeb Storageを利用して一時的にデータを保存する方法

メモリ内状態コンテナー サービス

入れ子になったコンポーネントと入れ子になっていないコンポーネントでは、登録済みのメモリ内状態コンテナーを使用してデータへのアクセスを共有できます。

Scopeについて
メソッド内容
AddTransient()インジェクション毎にインスタンスを生成
AddScoped()リクエスト毎にインスタンスを生成
AddSingleton()アプリケーション内で1つのインスタンスを生成
Program.cs
builder.Services.AddScoped<StateContainer>();

injectの記載方法

ディレクティブ記法でオブジェクトをインジェクトします。

xxxx.razor
@inject 【インジェクトするオブジェクトの型】 【インジェクトする変数名】
 
@inject StateService Service

上記の書き方以外にコードブックにて[Inject]属性を付与する記法があります。 今後コードビハインドで記述するならこっちの方が分かりやすいです。

xxxx.razor.cs
[Inject]
private StateService Service { get; set; } = default!;

物理パスの取得

Blazor Serverの場合、Directory.GetCurrentDirectory() を使用すればいい。

string rootpath = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), "wwwroot");

Web APIを追加

Blazor ServerにWeb APIを追加する。

サンプル

  1. Controllerフォルダを作成
  2. 右クリックメニューから[追加]の[コントローラー]をクリックする
  3. 共通のAPIにある「読み取り/書き込みアクションがある API コントローラー」を選択して[追加]ボタンをクリックする
  4. ValuesController.cs を追加する

下記コードをapp.Run()より前に、該当するところに追加する。

program.cs
// Add MVC Controllers
builder.Services.AddControllers();
 
app.MapRazorPages();
app.MapControllers();

Urlにてapiの後にコントローラー名を入れてアクセスすれば、値が返ってくる。

https://localhost:7072/api/Values

参照

HttpClientの使用

「@Inject HttpClient httpClient」を使用すると下記エラーが発生する。

Unhandled Promise Rejection: Error: System.InvalidOperationException: Cannot provide a value for property 'Http' on type . There is no registered service of type 'System.Net.Http.HttpClient'.

Injectを使用しないで、new HttpClient() とする。

ボタンクリックイベントでSUBMIT処理

ボタンクリックイベントでは、F1キーを入力させたようにしたいため、IsSubmit(type=“submit”)を使わないで制御したい。

<BSButton Color="BSColor.Info" Class="font-adjust" IsSubmit="true"><BSButton Color="BSColor.Info" Class="font-adjust" OnClick="@OnMenuClick">

メニューボタンをクリックしたら、入力値にF1キーをセットしてSUBMIT処理を呼ぶ。

public void OnMenuClick()
{
    MatchViewModel Model = new();
    Model.Input1 = Const.INPUT_KEY_F1;
 
    EditContext e = new(Model);
    OnSubmit(e);
}

カスタムコンポーネントのbind-value

bind-value を指定したたけでは、”does not have a property matching the name 'ValueChanged'“エラーとなり動作しない。
そのため、ParameterにValueChangedを定義する。

Index.razor
<CustomInput @bind-Value="@InputValue"></CustomInput>
 
@code { 
    public string InputValue = "Example";
}
CustomInput.razor
<input value="@Value" @oninput="@OnInputChange" />
<h4>Welcome to Blazor Application, @Value</h4>
 
@code {
    [Parameter]
    public string? Value { get; set; }
 
    [Parameter]
    public EventCallback<string> ValueChanged { get; set; }
    private async Task OnInputChange(ChangeEventArgs args )
    {
        Value = (string?)args.Value;
        await ValueChanged.InvokeAsync(Value);
    }
}

How do you use bind-value and bind-value:event on a custom component?

カスタムコンポーネントで親コンポーネントの処理を実行

カスタムコンポーネントのOnClickCallbackパラメータに親側の実行処理を記載する。
カスタムコンポーネント側のボタンをクリックすると親側の処理が実行される。

// イベント処理をラムダ式で書いた場合
<BSButton OnClick=@(async () => await OnClickCallback.InvokeAsync()>
 
@code {
    [Parameter]
    public EventCallback OnClickCallback { get; set; }
}

【Blazor】EventCallbackで子コンポーネントからイベントを受け取る方法

Javascriptとの連携

BlazorとjQueryの連携

下記のようにすれば、onchangeイベントを呼ぶことが出来る。この方法だと DotNet.invokeMethodAsync を使わずに済む。

combotree.js
$("#justAnotherInputBox").on('change', function () {
    var myElement = $(this)[0];
    var event = new Event('change');
    myElement.dispatchEvent(event);
    console.log("Change");
});
combotree.razor.cs
<input type="text" id="justAnotherInputBox" class="form-control" autocomplete="off" placeholder="Select"  @onchange="OnChange"/>
 
private void OnChange(ChangeEventArgs e)
{
    if (_comboTree != null || e.Value != null)
    {
        Console.WriteLine(e.Value);
    }
}

@ref 属性による弊害

inputタグに @ref 属性を付けて、 @ref=“CurrentElement” として、JQuery Pluginである ComboTree に渡していた。これで問題なく表示して動作していたが、同じ画面を再度表示しようとした際に this._input.addClass や this._input.wrap で例外エラーが発生してしまう。どうも追加するinputタグが見つけれない状態

_comboTree = await _jsClass.InvokeAsync<IJSObjectReference>("createComboTree", CurrentElement, options);
createComboTree(element, Options) {
    this.combo = $(element).comboTree(Options);

@ref 属性ではなく、一般的なid属性の使用に切り替えることで例外エラーが発生しなくなった。

_comboTree = await _jsClass.InvokeAsync<IJSObjectReference>("createComboTree", "#" + ElementId, options);

ログ出力にlog4netを使用する

Structured Logging in ASP.NET Core With log4net

log4net.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <log4net>
      <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
        <param name="Encoding" value="utf-8" />
        <!-- ファイル名 -->
        <param name="File" value="../../../logs/KEYENCE/ServerLog/IIS" />
        <!-- 追記する場合true/上書きする場合false -->
        <param name="AppendToFile" value="true" />
        <!-- 日付や時刻の制約によるログファイルの切替 -->
        <param name="RollingStyle" value="Date" />
        <param name="DatePattern" value='"."yyyy-MM-dd-HH".log"' />
        <param name="StaticLogFileName" value="false" />
        <layout type="log4net.Layout.PatternLayout">
          <param name="ConversionPattern" value="%d [%t] %-5p %c [%x] - %m%n" />
        </layout>
      </appender>
      <root>
        <level value="ALL" />
          <appender-ref ref="RollingFileAppender" />
      </root>
    </log4net>
</configuration>

デバッグ詳細

an unhandled exception on the current circuit のエラーの場合、詳細エラーを出力

https://stackoverflow.com/questions/57514541/how-to-turn-on-circuitoptions-detailederrors

appsettings,json
{
  "DetailedErrors": true, // turns on CircuitOptions.DetailedErrors
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information",
      "Microsoft.AspNetCore.SignalR": "Debug" // turns on SignalR debugging
    }
  },
  "AllowedHosts": "*"
}

ブラウザのキャッシュ回避

Blazorには、ASP.NET Coreにあった「asp-append-version=“true”」 が見当たらない。
Razor Pagesでファイル更新時にブラウザキャッシュを更新する方法

よって、ファイルバージョンを付けて回避している。

App.razor
<link rel="stylesheet" href="css/app.css?@Version" />
 
@code {
    private string Version = "v=" + FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).ProductVersion + "";
}

WebSocketプロトコルを使用する

[サーバーの役割] ページで、[Web サーバー (IIS)] を展開し、[Web サーバー]、[アプリケーション開発] の順に展開し、[WebSocket プロトコル] を選択して、インストールする。

Blazor Server は、WebSocket では通信できない環境であっても、Long Polling 方式に動作を切り替えて、動作継続します。
WebSocket で通信できない環境では Blazor Server は動作しないのか?

※WebSocketを使用した場合、IISログファイルに通信ログが残らなくなる。

参照

it技術/web開発/blzaor.txt · 最終更新: 2024/03/22 15:04 by yajuadmin