私のブレザーアプリでは、時間がかかる可能性のあるバックエンドサーバーに対してapi呼び出しを行っています。ユーザーへのフィードバック、待機カーソル、または「スピナー」画像を表示する必要があります。これはブレイザーでどのように行われますか?
CSSを使用してCSSのオンとオフを切り替えましたが、呼び出しが完了するまでページは更新されません。任意の提案をいただければ幸いです。
@functions {
UserModel userModel = new UserModel();
Response response = new Response();
string errorCss = "errorOff";
string cursorCSS = "cursorSpinOff";
protected void Submit()
{
//Show Sending...
cursorCSS = "";
this.StateHasChanged();
response = Service.Post(userModel);
if (response.Errors.Any())
{
errorCss = "errorOn";
}
//turn sending off
cursorCSS = "cursorSpinOff";
this.StateHasChanged();
}
}
2020年1月に編集。 @ Ed Charbenea で正しい方法を公開 BlazorPro.Spinkitプロジェクト :スレッドをブロックしないように長いプロセスをタスクに含める。彼の説明の前に、私はawait Task.Delay(1);
を使用してUIの変更をフラッシュしましたが、今、正しい方法はここに公開されています:
LongOperation()
がTask
であることを確認します。そうでない場合は、Task
で囲み、それを待ちます。
response = await Task.Run(()=> Service.Post(userModel)); //<--here!
Blazorは仮想domで動作し、フレームワークは変更を追跡し、メインスレッドがブロックされていない場合にのみ変更を送信します。これは私がBlazorに変更をUIにフラッシュさせる方法です:
async
関数を使用します。OPコードの場合:
protected async Task Submit()
{
//Show Sending...
cursorCSS = "";
this.StateHasChanged();
response = await Task.Run(()=> Service.Post(userModel)); //<--here!
if (response.Errors.Any())
{
errorCss = "errorOn";
}
//turn sending off
cursorCSS = "cursorSpinOff";
this.StateHasChanged();
}
例
async Task AsyncLongOperation() // this is an async task
{
spinning=true;
await Task.Run(()=> LongOperation()); //<--here!
currentCount++;
spinning=false;
}
ご覧のとおり、StateHasChanged
は必要ありません。
効果:
@page "/counter"
<h1>Counter</h1>
<p>Current count:
@(spinning?"Incrementing .... (the spinning effect)":currentCount.ToString())
</p>
<button class="btn btn-primary"
@onclick="@IncrementCount">Click me</button>
<button class="btn @(spinning?"btn-dark":"btn-primary") "
@onclick="@AsyncLongOperation">Click me Async</button>
@code {
int currentCount = 0;
bool spinning = false;
void IncrementCount()
{
currentCount++;
}
async Task AsyncLongOperation()
{
spinning=true;
await Task.Run(()=> LongOperation()); //<--here!
currentCount++;
spinning=false;
await Task.CompletedTask;
}
void LongOperation() => Task.Delay(2000).Wait();
}
Blazor Serverアプリは事前レンダリングを使用するため、スピナーは表示されません。スピナーを表示するには、長い操作をOnAfterRenderで実行する必要があります。
OnInitializeAsyncではなくOnAfterRenderAsyncを使用して、サーバー側レンダリングの遅延を回避します。
// Don't do this
//protected override async Task OnInitializedAsync()
//{
// await LongOperation();
//}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await LongOperation();
await Task.Run(()=> LongOperation()); //<--here!
StateHasChanged();
}
}
オープンソースプロジェクトから学ぶことができる、Niceスピナーの作成方法の詳細 BlazorPro.Spinkit には、次のような巧妙なサンプルが含まれています。
async Task TryLoadingData(Action<WeatherForecast[]> onSuccess,
Action<Exception> onFaulted)
{
isLoading = true;
try
{
if (forceException)
{
throw new NotSupportedException();
}
var data = await Task.Run(() => WeatherService.GetForecast(DateTime.Now));
isFaulted = false;
onSuccess(data);
}
catch (Exception e)
{
isFaulted = true;
onFaulted(e);
}
finally
{
isLoading = false;
}
}
以下は、BlazorテンプレートからのFetchData.razorファイルの内容です。
このファイルには2つの部分が含まれていることに注意してください。C#と混合されたHTML(Razor)、および@codeブロック内のC#コードでは、予測と呼ばれるWeatherForecastオブジェクトの配列を定義します。この配列は、OnInitAsyncメソッドで行われたhttp呼び出しからサーバーに返されるWeatherForecastオブジェクトを保持します。
Ifステートメント(@if (forecasts == null)
)がWeatherForecastオブジェクトが既に取得されているかどうかを確認していることに注意してください。変数の予測がnullである限り、html <p><em>Loading...</em></p>
表示されています。ここには、画像、スピナーなどを含め、好きなだけHTMLを追加できます。
天気予報がWeatherForecastオブジェクトに割り当てられると、Htmlテーブルが取得されたデータとともに表示されます
お役に立てれば...
@page "/fetchdata"
@using BlazorHosted_CSharp.Shared
@inject HttpClient Http
<h1>Weather forecast</h1>
<p>This component demonstrates fetching data from the server.</p>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
@code {
WeatherForecast[] forecasts;
protected override async Task OnInitAsync()
{
forecasts = await Http.GetJsonAsync<WeatherForecast[]>("api/SampleData/WeatherForecasts");
}
}
@ daniherrera's solution の通知に答えるために、3つのよりエレガントなソリューションが提案されています here 。
要するに :
INotifyPropertyChanged
して呼び出しStateHasChanged()
onPropertyChangedEventHandler
モデルのイベントプロパティ。StateHasChanged()
EventCallBack<T>
_パラメータをビューのコンポーネントまたはページに追加し、それを次の関数に割り当てます。コンポーネントとその親のレンダリングを変更する必要があります。(StateHasChanged()
はありませんこれで必要 `)最後のオプションは、最も単純で柔軟性があり、高レベルですが、必要に応じて選択してください。
全体として、アプリのセキュリティが懸念される場合は、await Task.Delay(1);
ソリューションよりも提示されたソリューションの1つを使用することをお勧めします。
編集:さらに読んだ後、 このリンク はC#でイベントを処理する方法について、主にEventCallBack
。
Thread.Sleep(n)を使用して待機スピナーをテストしたときと同じ間違いをしないでください。
protected override async Task OnInitializedAsync()
{
// Thread.Sleep(3000); // By suspending current thread the browser will freeze.
await Task.Delay(3000); // This is your friend as dani herrera pointed out.
// It creates a new task that completes
// after a specified number of milliseconds.
forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
}