.NET Magazine國際中文電子雜誌
作 者:許薰尹
審 稿:張智凱
文章編號:N241026603
出刊日期:2024/10/30
本篇文章將延續本站《.NET 8 Blazor入門 - 1》一文的情境,介紹如何使用Visual Studio 2022開發工具,一步、步帶領你建立一個.NET 8 Blazor的員工系統,使用SPA架構來進行資料的新增、刪除、修改與查詢作業。
回顧我們在《.NET 8 Blazor入門 - 1》一文建立的Blazor Web App專案,目前首頁「Home.razor」程式如下,使用表格顯示員工清單,不過在元件的@code程式碼區段之中,直接定義存放員工資料的「employees」陣列:
@page "/"
@using HRApp.Models
<PageTitle> HR App </PageTitle>
<table class = "table table-striped">
<thead>
<tr>
<th> Employee ID </th>
<th> Employee Name </th>
<th> Birthday </th>
<th> Marital Status </th>
<th> Job Type </th>
</tr>
</thead>
<tbody>
@foreach ( var employee in employees ) {
<tr>
<td> @employee.Id </td>
<td> @employee.EmployeeName </td>
<td> @employee.BirthDay.ToShortDateString( ) </td>
<td> @( employee.IsMarried ? "Married" : "Single" ) </td>
<td> @employee.JobType </td>
</tr>
}
</tbody>
</table>
@code {
private Employee[] employees = new Employee[] {
new Employee { Id = 1, EmployeeName = "John Doe", BirthDay = new DateOnly(1990, 5, 15), IsMarried = true, JobType = "Manager" },
new Employee { Id = 2, EmployeeName = "Jane Smith", BirthDay = new DateOnly(1985, 10, 20), IsMarried = false, JobType = "Developer" },
new Employee { Id = 3, EmployeeName = "Mike Johnson", BirthDay = new DateOnly(1992, 3, 8), IsMarried = true, JobType = "Designer" },
new Employee { Id = 4, EmployeeName = "Emily Davis", BirthDay = new DateOnly(1998, 7, 12), IsMarried = false, JobType = "Tester" },
new Employee { Id = 5, EmployeeName = "David Wilson", BirthDay = new DateOnly(1994, 9, 25), IsMarried = true, JobType = "Developer" }
};
}
這不是一個好做法,例如,若有兩個以上的元件要使用到相同的員工資料,目前的做法無法共用這些資料,我們應該將提供資料的程式抽出來寫在另外一個類別之中,例如包裝成服務(Service)的程式碼。
設計服務
服務類別建議放在「Services」資料夾中。從Visual Studio開發工具>「Solution Explorer」視窗>專案名稱上方按滑鼠右鍵,從快捷選單選擇「Add」>「New Folder」選項,將新建立的資料夾命名為「Services」。
在「Solution Explorer」視窗 >「Services」資料夾名稱上按滑鼠右鍵,從快捷選單選擇「Add」> 「Class」,開啟「Add New Item」對話盒,選取「Class」選項,設定名稱為「EmployeesService.cs」,然後按下「Add」按鈕,建立新類別。然後在類別中加入以下程式碼,定義一個名為「EmployeesService」的服務類別,該類別負責管理員工資料。:
using HRApp.Models;
namespace HRApp.Services {
public class EmployeesService {
private readonly List<Employee> employees = [
new Employee { Id = 1, EmployeeName = "John Doe", BirthDay = new DateOnly(1990, 5, 15), IsMarried = true, JobType = "Manager" },
new Employee { Id = 2, EmployeeName = "Jane Smith", BirthDay = new DateOnly(1985, 10, 20), IsMarried = false, JobType = "Developer" },
new Employee { Id = 3, EmployeeName = "Mike Johnson", BirthDay = new DateOnly(1992, 3, 8), IsMarried = true, JobType = "Designer" },
new Employee { Id = 4, EmployeeName = "Emily Davis", BirthDay = new DateOnly(1998, 7, 12), IsMarried = false, JobType = "Tester" },
new Employee { Id = 5, EmployeeName = "David Wilson", BirthDay = new DateOnly(1994, 9, 25), IsMarried = true, JobType = "Developer" }
];
public Employee[] GetEmployees( ) => [.. employees];
}
}
「EmployeesService」類別中定義了一個私有且唯讀的「employees」集合,並初始化了五個「Employee」物件。每個「Employee」物件都有「Id」、「EmployeeName」、「BirthDay」、「IsMarried」和「JobType」屬性。
「Employee」類別中定義了一個「GetEmployees」方法,回傳一個「Employee」陣列,其中包含了「employees」集合中的所有員工資料。
修改「Home.razor」元件程式如下:
@page "/"
@using HRApp.Models
@using HRApp.Services
<PageTitle> HR App </PageTitle>
@if ( employees is null ) {
<p> <em> Loading... </em> </p>
}
else {
<table class = "table table-striped">
<thead>
<tr>
<th> Employee ID </th>
<th> Employee Name </th>
<th> Birthday </th>
<th> Marital Status </th>
<th> Job Type </th>
</tr>
</thead>
<tbody>
@foreach ( var employee in employees ) {
<tr>
<td> @employee.Id </td>
<td> @employee.EmployeeName </td>
<td> @employee.BirthDay.ToShortDateString( ) </td>
<td> @( employee.IsMarried ? "Married" : "Single" ) </td>
<td> @employee.JobType </td>
</tr>
}
</tbody>
</table>
}
@code {
private EmployeesService service = new( );
private Employee[]? employees;
protected override void OnInitialized( ) {
employees = service.GetEmployees( );
}
}
因為元件以非同步方式執行,我們需要在取得員工陣列的情況下再顯示表格,可利用「@if」區塊程式碼檢查「employees」是否為「null」。如果是,則顯示「Loading...」 信息;否則則顯示員工列表。
「@code」區塊程式碼中建立了一個「EmployeesService」物件。「OnInitialized」方法會在元件初始化時自動被叫用,當被叫用時,便從「EmployeesService」物件的「GetEmployees」方法取得員工資料放到「employees」變數。
在 Visual Studio 2022 開發工具中,按下 鍵盤CTRL+F5組合鍵 執行網站。目前首頁顯示的是專案中「Home.razor」元件的執行結果,請參考下圖所示:
圖 1:「Home.razor」元件的執行結果。
新增員工資料
現在我們來談談如何新增員工資料,目前專案中「Employee」模型類別的定義如下:
namespace HRApp.Models {
public class Employee {
public int Id { get; set; }
public required string EmployeeName { get; set; }
public DateOnly BirthDay { get; set; }
public bool IsMarried { get; set; }
public required string JobType { get; set; }
}
}
為了搜集有效的資料,當使用者在網頁中新增員工資料時,我們希望「JobType」要搭配下拉式選單讓使用者從現有清單中挑選員工類型,因此我們另外在「Models」資料夾定義一個「EmployeeDetails」模型類別對應到使用者介面,其中包含「string?」型別的「JobTypeId」屬性:
namespace HRApp.Models {
public class EmployeeDetails {
public int Id { get; set; }
public required string EmployeeName { get; set; }
public DateOnly BirthDay { get; set; }
public bool IsMarried { get; set; }
public string? JobTypeId { get; set; }
}
}
從Visual Studio開發工具>「Solution Explorer」視窗>「Pages」資料夾上方按滑鼠右鍵,從快捷選單選擇「Add」>「Razor Component」選項,建立元件,將新建立的檔案命名為「EditEmployee.razor」。
「EditEmployee.razor」元件包含了負責顯示和編輯員工資料的表單,在「EditEmployee.razor」加入以下程式碼:
@page "/editemployee"
@using Models
<PageTitle> New Employee </PageTitle>
<h3> New Employee </h3>
<div class = "row">
<div class = "col-md-8 col-lg-12">
<EditForm Model = "@employee" FormName = "editEmployee">
<div class = "mb-3">
<label for = "employeeName" class = "form-label"> Employee Name : </label>
<InputText id = "employeeName" @bind-Value = "employee.EmployeeName" class = "form-control" />
</div>
<div class = "mb-3">
<label for = "birthDay" class = "form-label"> Birth Day : </label>
<InputDate id = "birthDay" @bind-Value = "employee.BirthDay" class = "form-control" />
</div>
<div class = "mb-3">
<label for = "isMarried" class = "form-label"> Is Married : </label>
<InputCheckbox id = "isMarried" @bind-Value = "employee.IsMarried" class = "form-check-input" />
</div>
<div class = "mb-3">
<label for = "jobType" class = "form-label"> Job Type : </label>
<InputSelect id = "jobType" @bind-Value = "employee.JobTypeId" class = "form-select">
</InputSelect>
</div>
<button type = "submit" class = "btn btn-primary"> Submit </button>
</EditForm>
</div>
</div>
@code {
private EmployeeDetails employee { get; set; } = new EmployeeDetails( ) {
EmployeeName = string.Empty ,
BirthDay = new DateOnly( 2000 , 1 , 1 )
};
}
程式說明如下,「@page 」這行程式碼碼指定了這個元件的路由為「/editemployee」:
@page "/editemployee"
以下的「div」元素使用 Bootstrap 的網格系統(Grid System)來設計表單版面佈局(Layout):
<div class = "row">
<div class = "col-md-8 col-lg-12">
...
下面這個「EditForm」元件繫結到「employee」模型,並且使用「FormName」將表單名稱為「editEmployee」:
<EditForm Model = "@employee" FormName = "editEmployee">
下面這行程式碼使用「label」標籤顯示「Employee Name :」提示字串:
<label for = "employeeName" class = "form-label"> Employee Name : </label>
下面這行程式碼使用「InputText」元件繫結到「employee.EmployeeName」屬性:
<InputText id = "employeeName" @bind-Value = "employee.EmployeeName" class = "form-control" />
下面這行程式碼使用「InputDate」元件繫結到「employee.BirthDay」屬性:
<InputDate id = "birthDay" @bind-Value = "employee.BirthDay" class = "form-control" />
下面這行程式碼使用「InputCheckbox」元件繫結到「employee.IsMarried」屬性:
<InputCheckbox id = "isMarried" @bind-Value = "employee.IsMarried" class = "form-check-input" />
下面這行程式碼使用「InputSelect」元件繫結到「employee.JobTypeId」屬性:
<InputSelect id = "jobType" @bind-Value = "employee.JobTypeId" class = "form-select">
</InputSelect>
這個按鈕用來提交表單:
<button type = "submit" class = "btn btn-primary"> Submit </button>
「@code」區塊中定義並初始化了一個「EmployeeDetails」物件,並設定「EmployeeName」、「BirthDay」屬性的初始值:
@code {
private EmployeeDetails employee { get; set; } = new EmployeeDetails( ) {
EmployeeName = string.Empty ,
= new DateOnly( 2000 , 1 , 1 )
};
}
在 Visual Studio 2022 開發工具中,按下 鍵盤CTRL+F5組合鍵執行網站。在瀏覽器輸入URL:「http://localhost:埠號/editemployee」,將顯示修改員工的畫面,執行結果請參考下圖所示:
圖 2:修改員工資料畫面。
產生下拉式清單選項
現在我們要在員工資料修改畫面中,將員工類型的「JobTypeId」變成下拉式清單,讓使用者從清單中選擇,避免使用者輸入無效資料。在「Models」資料夾加入一個「JobType」類別,然後加入程式碼如下:
namespace HRApp.Models {
public class JobType {
public int Id { get; set; }
public required string Type { get; set; }
}
}
定義了一個公開的「JobType」類別,有兩個屬性:「Id」用來唯一標識每個工作類型;「Type」用來描述工作類型的名稱,並且它是必需的、不可不填。
在「Services」資料夾加入一個「JobTypeService」類別,然後加入程式碼如下:
using HRApp.Models;
namespace HRApp.Services {
public class JobTypeService {
private readonly JobType[] jobTypes = [
new JobType { Id = 1, Type = "Manager" },
new JobType { Id = 2, Type = "Developer" },
new JobType { Id = 3, Type = "Designer" },
new JobType { Id = 4, Type = "Tester" }
];
public JobType[] GetJobTypes( ) => jobTypes;
}
}
「JobTypeService」類別負責管理和提供工作類型資料。類別中定義了一個私有、且唯讀的「jobTypes」變數,類型為「JobType」陣列。這個陣列包含四個「JobType」物件,每一個物件都有「Id」和 「Type」 屬性。
「JobTypeService」類別還定義一個「GetJobTypes()」方法,這個方法回傳一個「JobType」陣列,包含了「jobTypes」變數中的所有工作類型。
下一步修改「EditEmployee.razor」程式碼如下:
@page "/editemployee"
@using HRApp.Services
@using Models
<PageTitle> New Employee </PageTitle>
<h3> New Employee </h3>
<div class = "row">
<div class = "col-md-8 col-lg-12">
<EditForm Model = "@employee" FormName = "editEmployee">
<div class = "mb-3">
<label for = "employeeName" class = "form-label"> Employee Name : </label>
<InputText id = "employeeName" @bind-Value = "employee.EmployeeName" class = "form-control" />
</div>
<div class = "mb-3">
<label for = "birthDay" class = "form-label"> Birth Day : </label>
<InputDate id = "birthDay" @bind-Value = "employee.BirthDay" class = "form-control" />
</div>
<div class = "mb-3">
<label for = "isMarried" class = "form-label"> Is Married : </label>
<InputCheckbox id = "isMarried" @bind-Value = "employee.IsMarried" class = "form-check-input" />
</div>
<div class = "mb-3">
<label for = "jobType" class = "form-label"> Job Type : </label>
<InputSelect id = "jobType" @bind-Value = "employee.JobTypeId" class = "form-select">
<option> Select Job Type </option>
@foreach ( var jobType in jobTypes! ) {
<option value = "@jobType.Id"> @jobType.Type </option>
}
</InputSelect>
</div>
<button type = "submit" class = "btn btn-primary"> Submit </button>
</EditForm>
</div>
</div>
@code {
private EmployeeDetails employee { get; set; } = new EmployeeDetails( ) {
EmployeeName = string.Empty ,
BirthDay = new DateOnly( 2000 , 1 , 1 )
};
private JobTypeService jobTypeService = new( );
private JobType[]? jobTypes;
protected override void OnInitialized( ) {
jobTypes = jobTypeService.GetJobTypes( );
}
}
參考以下程式碼,「InputSelect」元件是一個下拉選單,「option」標籤用來加入一個預設選項,顯示「Select Job Type」提示字串。這個選項沒有設定值,當使用者沒有選擇工作類型時會顯示這個選項。下拉選單中其餘的「option」選項是利用「foreach」語法根據「jobTypes」陣列中的內容動態產生的。
<InputSelect id = "jobType" @bind-Value = "employee.JobTypeId" class = "form-select">
<option> Select Job Type </option>
@foreach ( var jobType in jobTypes! ) {
<option value = "@jobType.Id"> @jobType.Type </option>
}
</InputSelect>
「@code」區塊中初始化和載入工作類型資料,以便在元件中使用。我們建立「JobTypeService」物件來獲取工作類型資料。
private JobTypeService jobTypeService = new( );
定義一個「jobTypes」變數,型別是「JobType[]?」,用來儲存工作類型資料。
private JobType[]? jobTypes;
最後在「OnInitialized」方法中,叫用「JobTypeService」物件的「GetJobTypes」方法,將獲取到的工作類型資料放到「jobTypes」變數:
protected override void OnInitialized( ) {
jobTypes = jobTypeService.GetJobTypes( );
}
這樣當「EditEmployee」元件初始化時,「jobTypes」變數就會包含所有的工作類型資料,這些資料將應用在下拉選單中使用。
在 Visual Studio 2022 開發工具中,按下 鍵盤CTRL+F5組合鍵執行網站。在瀏覽器直接輸入URL:「http://localhost:埠號/editemployee」,瀏覽器將顯示修改員工的畫面,執行結果請參考下圖所示:
圖 3:產生下拉式清單選項。