Blazor tutorial for beginners - part 2

以 Blazor Server App 入門教學為主。

JS Interop: Calling JavaScript from C# | Blazor Tutorial 5

  • IJSRuntime: You can call JS code from C#

  • JSInvokableAttribute:You call call C# code from JS

JS Interop : JavaScript Interoperability

Pages/JSInteropDemo.razor
@page "/jsinterop"
@inject IJSRuntime jsRuntime

<h1>JavaScript Interoperability Demo</h1>

<h4>Show Alert</h4>
<input @bind-value="message" />
<button class="btn btn-success" @onclick="ShowAlert">Show Alert</button>

<h4>Call Prompt</h4>
<p>一進入畫面就 focus 到這裡。</p>
<input @ref="inputRef" @bind-value="question" />
<button class="btn btn-warning" @onclick="AskQuestion">Ask question</button>
<div>
    答案是:<span style="color:mediumvioletred" id="answerSpan"></span>
</div>

<h4>Foucs Element</h4>
<button class="btn btn-secondary" @onclick="FocusElement">Focus on Call Prompt</button>

@code{

    string message = "我是來自C#的文字訊息。";
    string question = "今天象氣如何?";
    ElementReference inputRef;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender) {
            await FocusElement();
        }
    }

    async Task ShowAlert()
    {
        //string msg = "我是來自C#的文字訊息。";
        await jsRuntime.InvokeVoidAsync("showAlert", message);
    }

    async Task AskQuestion()
    {
        var response = await jsRuntime.InvokeAsync<string>("showPrompt", question);
        await jsRuntime.InvokeVoidAsync("setElementTextById", "answerSpan", response);
    }

    async Task FocusElement()
    {
        await jsRuntime.InvokeVoidAsync("focusElement", inputRef);
    }
}
js/interop.js
function showAlert(msg) {
  alert(msg);
}

function showPrompt(question) {
  return prompt(question);
}

function setElementTextById(id, text) {
  document.getElementById(id).innerText = text;
}

function focusElement(element) {
  element.focus();
}

JS Interop: Calling C# methods from JavaScript | Blazor Tutorial 6

由 JS 為啟始向 C# 呼叫函式(資源)應該是不會有此應用情境的,實務上應該用不到也不建議採用JS call CS code,因為 Blazor 的前端資源並不豐富且程式碼繞好大一圈。

但是,由 C# 為啟始向 JS 呼叫函式(資源)現況是必然的,再由 JS 向 C# 呼叫回覆函式也是常有的應用情境, ,因為前端資訊交換機制常是非同步 event-base 的。

C# ---(call/ask resource)---> JS 
JS do processing
JS ---(invoke/reply message)---> C#

下面範例有

  • IJSRuntime

    • to call global JS function.

  • IJSObjectReference

    • import JavaScript function.

    • used to invoke JS function that imported.

  • DotNetObjectReferenceJSInvokable

    • used to let JS invoke the C# function.

程式碼紀錄:IdleWidget.razor、GlobalIdleTimer.js

目的:idle 時間太長自登出。

透過IJSObjectReferenceIJSRuntime介面,動態載入 JS moudle。 使用ElementReference傳遞 Element 給 JavaScript 更新。 使用 DotNetObjectReference傳遞JSInvokable函式讓JavaScript可以回呼(callback)。

Shared/IdleWidget.razor
@using Microsoft.JSInterop.Implementation
@inject IJSRuntime jsr
@inject NavigationManager navMan

<span @ref="displayRef" class="mx-1">idle</span>

@code{
    #region Resource
    IJSObjectReference module;
    ElementReference displayRef;
    #endregion

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await base.OnAfterRenderAsync(firstRender);

        if (firstRender)
        {
            // 動態載入 JS module
            module = await jsr.InvokeAsync<IJSObjectReference>("import", "./js/GlobalIdleTimer.js");

            // 啟動 Idel 計數器
            await module.InvokeVoidAsync("Start", DotNetObjectReference.Create(this), displayRef);
        }
    }

    /// <summary>
    /// 當Idel計數到達時觸發
    /// </summary>
    [JSInvokable] //--- 讓JavaScript可以回呼
    public Task<int> OnIdleTimeout(int _idleCount)
    {
        //## idle 時間太長自動登出。
        navMan.NavigateTo("/Signout"); // 登出
        return Task.FromResult(0);
    }
}

監測使用者是否有在使用滑鼠鍵盤等輸入,並累計idle時間,若達到門檻就觸發訊息以登出系統。

./js/GlobalIdleTimer.js
/// state
let idle_timer = 0;
const IDLE_TIMEOUT = 5 * 20; // 5分鐘。三秒累計1次則每分鐘累計20次;

/// resource holder
let intervalId;    // timer handler
let _dotNetObject; // 將指向來自C#的Razor元件
let _displayRef;   // 將指向element

export function Start(dotNetObject, displayRef) {
  _dotNetObject = dotNetObject;
  _displayRef = displayRef;
  document.addEventListener('click', resetGlobalIdleIimer);
  document.addEventListener('mousemove', resetGlobalIdleIimer);
  document.addEventListener('mouseenter', resetGlobalIdleIimer);
  document.addEventListener('keydown', resetGlobalIdleIimer);
  document.addEventListener('scroll', resetGlobalIdleIimer);
  document.addEventListener('touchstart', resetGlobalIdleIimer);
  intervalId = setInterval(increaseGlobalIdleIimer, 3000); // 每3秒累加一次計數
};

//export function Release() {
//  clearInterval(intervalId);
//  document.removeEventListener('click', resetGlobalIdleIimer);
//  document.removeEventListener('mousemove', resetGlobalIdleIimer);
//  document.removeEventListener('mouseenter', resetGlobalIdleIimer);
//  document.removeEventListener('keydown', resetGlobalIdleIimer);
//  document.removeEventListener('scroll', resetGlobalIdleIimer);
//  document.removeEventListener('touchstart', resetGlobalIdleIimer);
//}

function increaseGlobalIdleIimer() {
  idle_timer++;
  //console.log('idle_timer', idle_timer);

  // 更新UI
  if (_displayRef)
    _displayRef.innerHTML = idle_timer;

  //## idle timeout,誤差為一次計數
  if (idle_timer > IDLE_TIMEOUT) {
    // 達到idle門檻,送出訊息以登出系統。
    _dotNetObject.invokeMethodAsync('OnIdleTimeout', idle_timer);
  }
}

function resetGlobalIdleIimer() {
  idle_timer = 0;
  //console.log('idle_timer', idle_timer);
}

Creating Forms with Validation | Blazor Tutorial 7

詳情請參考附件。

Blazor 提供了幾個表單操作元件系結表單:

  • <EditForm />取代 <form /> 標籤。

  • <DataAnnotationValidator />

  • <ValidationSummary/>

  • <ValidationMessage/>

也提供內建表單 input (Built-in form components)元件:

輸入元件

呈現為…

<input type="checkbox">

<input type="date">

<input type="file">

<input type="number">

<input type="radio">

<input>

<textarea>

簡單的表單範例

表單的驗證也支援 DataAnnotations 不過在此就懶的再記錄一次,請謨拜 Google 大神。

@page "/form-example-1"
@using Microsoft.Extensions.Logging
@inject ILogger<FormExample1> Logger

<EditForm Model="exampleModel" OnValidSubmit="HandleValidSubmit">
    <DataAnnotationsValidator />
    <ValidationSummary />

    <InputText id="name" @bind-Value="exampleModel.Name" />
    <ValidationMessage For="@(()=>exampleModel.Name)" />

    <button type="submit">Submit</button>
</EditForm>

@code {
    private ExampleModel exampleModel = new();

    private void HandleValidSubmit()
    {
        Logger.LogInformation("HandleValidSubmit called");

        // Process the valid form
    }
}

Last updated