(6/30)大家一起學Blazor:抽離C#程式碼

(6/30)大家一起學Blazor:抽離C#程式碼

昨天看到`FetchData.razor`的程式碼太長了,為求方便,我們把`@code`的部分抽取出來另成一個檔案。

最後更新 2021/12/12 下午9:59
StrayaWorker
預計閱讀 6 分鐘
分類
Blazor
專題
一起學Blazor系列
標籤
.NET C# ASP.NET Core Blazor

昨天看到FetchData.razor的程式碼太長了,為求方便,我們把@code的部分抽取出來另成一個檔案。

Blazor 提供了兩個方法:partial classComponentBase,同樣兩者各有優缺點,筆者偏好ComponentBase,看個人使用習慣。

先來看partial class,我們打開FetchData.razor,把前面提到的@using BlazorServer.Data移到_Imports.razor,再建立一個class叫做FetchData.razor.cs,在publicclass中間加上partial修飾詞,接著把FetchData.razor@code區塊剪下貼到FetchData.razor.cs後稍作修改,可以看出跟舊的 C#程式碼沒什麼差別。我們按下 F5,一樣看到 10 條天氣資料。

原 FetchData.razor:

原FetchData.razor

提取 C#程式碼後程式碼截圖:

提取C#程式碼後程式碼截圖

頁面展示不變:

頁面展示不變

接著來看ComponentBase,先把partial class裡面的程式碼複製下來,接著建立一個新 class 取名為FetchDataBase.cs,將剛剛的程式碼貼上後稍做修改,再刪除FetchData.razor.cs這個 partial class,因為兩種模式不能並存。可以看到除了繼承ComponentBase跟加上[Inject]外,幾乎沒有差別,這裡的[Inject]相當於在FetchData.razor.cs使用@inject WeatherForecastService ForecastService,我們按下 F5 啟動偵錯模式,在 14 行左邊按一下點擊中斷點,可以看到一樣取得了天氣資料。

使用 ComponentBase:

使用ComponentBase

接著開始做自己的 Component 吧!先將不必要的 Component 刪除,Program.cs 和_Import.razor 裡面跟天氣有關的 using 及註冊同樣刪除,如果忘記這兩個檔案在哪裡,可以在刪除 Component 後將滑鼠移到專案 BlazorServer 點右鍵,選取重建專案,Visual Studio 就會告訴你哪裡出錯了。

刪除多餘元件等檔案:

刪除多餘元件等檔案

之前說過要完成讓使用者寫日誌的網站,所以需要最基本的輸入框,而日誌的單位就以一篇計算。首先建立 Models 資料夾,建立PostModel類型,裡面很簡單只有 3 個屬性,接著在 Pages 資料夾建立Post.razorPostBase.razor.cs,最後將NavMenu.razor的連線留下一個,href屬性的值改為Post

/Models/PostModel.cs

namespace BlazorServer.Models;

public class PostModel
{
	public int Id { get; set; }

	public string? Title { get; set; }

	public string? Content { get; set; }
}

/Shared/NavMenu.razor:

<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
  <nav class="flex-column">
    <div class="nav-item px-3">
      <NavLink class="nav-link" href="/Post" Match="NavLinkMatch.All">
        <span class="oi oi-home" aria-hidden="true"></span> Post
      </NavLink>
    </div>
  </nav>
</div>

PostModel是用來承接資料的容器,目前沒有 Service,所以在PostBase.razor.cs放一筆假資料,這邊可以看到一個方法OnInitializedAsync(),代表當這個 Component 生命週期開始,裡面的事情就會先做,其他還有OnAfterRenderAsyncOnParametersSetAsync等等,只要先輸入 override 再按一下空白鍵,就可以看到這些方法,且也有同步跟非同步兩種模式,這些方法有機會再說明。

PostBase

Post.razor用了EditForm這個 Component,編譯過後相當於 html 的 form 元素,裡面還有 3 個 input 元素,Blazor 也有提供相對應的 Input Component,可以從官方文件看到分別編譯後的 html 元素。

@page "/Post" @inherits PostBase

<EditForm Model="@Post">
  <input type="number" value="@Post!.Id" />
  <input type="text" value="@Post!.Title" />
  <textarea value="@Post!.Content"></textarea>

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

上下兩種寫法對比:

@page "/Post" @inherits PostBase

<EditForm Model="@Post">
  <InputNumber @bind-Value="Post!.Id"></InputNumber>
  <InputText @bind-Value="Post!.Title"></InputText>
  <InputTextArea @bind-Value="Post!.Content"></InputTextArea>

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

Blazor 提供相對應的 Input Component:

Input Component

這時候打開網頁來看,可以看到上面出現了我們定義在PostBase.razor.cs的值,但這是怎麼連接起來的呢?原因就是 EditForm 的 Model 屬性及 3 個<Input>Component 的屬性@bind-Value,這裡在告訴 Blazor:我的 Model 跟裡面的值要跟這個 EditForm 綁在一起,如果這裡有跟後端程式碼連接,網頁上輸入的內容經過事件觸發後,就會提交後端處理。

Post頁面展示

不過<Input>預設的 CSS 樣式不太好看,我們先套用基本的 boostrap 樣式;因為 Id 通常不會讓使用者輸入,所以這邊先註解,然後再加入表格驗證機制,畢竟不能讓使用者隨便輸入就提交表格,但如果不想自己寫一堆驗證機制呢?可以試試看 Blazor 的DataAnnotationsValidatorValidationSummary兩個 Component。

添加樣式

我們先在 PostModel 的 Title 跟 Content 加上兩個 Attribute,Required 代表必填,MaxLength 及 MinLength 則是限制最大及最小字數,還可以自訂錯誤訊息。接著在 EditForm 裡面加上那兩個 ComponentDataAnnotationsValidator 及 ValidationSummary,第一個是驗證各個 Input,第二個則是將錯誤訊息顯示在表格上方。

添加驗證

驗證錯誤提示

但如果不喜歡驗證機制預設的 CSS 樣式呢?Blazor 也提供了客製化的方法。先建立一個class名為CustomFieldClassProvider且繼承FieldCssClassProvider,多載方法GetFieldCssClass,裡面的內容待會再說。

using Microsoft.AspNetCore.Components.Forms;

namespace BlazorServer;

public class CustomFieldClassProvider : FieldCssClassProvider
{
	public override string GetFieldCssClass(EditContext editContext, in FieldIdentifier fieldIdentifier)
	{
		var isValid = !editContext.GetValidationMessages(fieldIdentifier).Any();

		return isValid ? "text-primary" : "text-danger";
	}
}

接著在PostBase.razor.cs加入一個型別為EditContext的欄位EditContext,初始化欄位EditContext,將原本的Post欄位丟進去,然後呼叫EditContext的方法SetFieldCssClassProvider,添加CustomFieldClassProvider實例。

自訂樣式關聯

最後最重要的一步,在Post.razorEditFormModel參數移除,改為EditContext參數,裡面的值就是剛剛的EditContext欄位。

替換Model為EditContext

這時候再提交表單一次,可以看到 textarea 的紅色外框消失了,字體也變成text-danger的紅色,而正確的欄位值則是變成text-primary的藍色。我們再回頭看CustomFieldClassProvider,原來EditContext指的就是EditForm的內容,fieldIdentifier則是當前驗證的Input標籤,如果EditContext呼叫的方法GetValidationMessages有在fieldIdentifier取得任何資訊,代表這是錯誤的欄位值,沒有則是正確欄位值,這就是 Blazor 幫我們客製化 Form 的做法。

客製的資料驗證提示

引用:

  1. Split HTML And C# Code In Blazor Using Either Partial Class Or ComponentBase Class
  2. ASP.NET Core Blazor forms and validation
  3. Custom validation CSS class attributes

註:本文程式碼透過 .NET 6 + Visual Studio 2022 重構,可點擊原文連結與重構後程式碼比較學習,謝謝閱讀,支持原作者

繼續探索

延伸閱讀

更多文章
同分類 / 同標籤 2021/12/25

(29/30)大家一起學Blazor:Blazor單元測試

開發一個系統最無聊的過程大概就是解決 Bug 了,尤其是那種嘗試對 null 物件取值的錯誤(`Object reference not set to an instance of an object.`),這應該是大部分人剛踏入程式設計領域最常碰到的問題,為了從枯燥的解決 Bug 過程解脫,這篇就來介紹`單元測試`。

繼續閱讀
同分類 / 同標籤 2021/12/25

(28/30)大家一起學Blazor:Policy-based authorization

之前有說到`ASP.NET Core Identity` 使用的是基於`Claim` 的驗證,其實`ASP.NET Core Identity` 有不同類型的授權方式,最簡單的`登入授權`、`角色授權`、`Claim 授權`,但上述幾種都是以一種方式實現:原則授權(`Policy-based authorization`)。

繼續閱讀