前言
大家好,我是開源專案 MASA Blazor 主要開發者之一,如果你還不了解MASA Blazor,可以訪問我們的 官網 和博客 《初識MASA Blazor》 一探究竟,簡單來說,MASA Blazor 是一個基于 Material Design 設計語言的 Blazor 組件庫,dotNET開發者只需或者甚至不需要懂得 javascript 就能開發一個企業級中后臺系統,
我這次分享的主題是《使用MASA Blazor開發一個標準的查詢表格頁》,我會先從創建專案開始手擼一個沒有任何技巧的查詢表格頁,然后我會分享一些技巧和封裝的組件,實作快速開發,
手擼查詢表格頁
創建應用程式
關于如何安裝MASA Blazor模板,請移步 MASA.Blazor快速入門,
-
首先通過MASA Blazor模板默認的Server專案,專案命名為 MasaBlazorStandardTablePage,
dotnet new --install MASA.Template dotnet new masab -o MasaBlazorStandardTablePage -
通過CLI運行應用程式,或直接通過vs啟動專案,
cd MasaBlazorStandardTablePage dotnet run -
啟動成功后切換到 Fetch data 頁面,此頁面展示了一個簡單的使用了
MDataTable的表格,
支持單個查詢條件和搜索
讓我們從最簡單的單個條件查詢開始,
將隨機資料替換成模擬資料
-
修改
WeatherForecastService,將隨機資料替換成死資料以便支持查詢功能,下面的代碼更新了資料來源和GetForecastAsync查詢方法,public class WeatherForecastService { private readonly List<WeatherForecast> _data = https://www.cnblogs.com/capdiem/p/new() { new() { Date = DateTime.Now.AddDays(-1), TemperatureC = 23, Summary ="Freezing" }, new() { Date = DateTime.Now.AddDays(-1), TemperatureC = -10, Summary = "Bracing" }, new() { Date = DateTime.Now.AddDays(-1), TemperatureC = 37, Summary = "Chilly" }, new() { Date = DateTime.Now.AddDays(-2), TemperatureC = 29, Summary = "Cool" }, new() { Date = DateTime.Now.AddDays(-3), TemperatureC = 11, Summary = "Mild" }, new() { Date = DateTime.Now.AddDays(-4), TemperatureC = 35, Summary = "Warm" }, new() { Date = DateTime.Now.AddDays(-5), TemperatureC = 41, Summary = "Balmy" }, new() { Date = DateTime.Now.AddDays(-5), TemperatureC = -13, Summary = "Hot" }, new() { Date = DateTime.Now.AddDays(-6), TemperatureC = 23, Summary = "Sweltering" }, new() { Date = DateTime.Now.AddDays(-7), TemperatureC = 2, Summary = "Scorching" }, }; public Task<WeatherForecast[]> GetForecastAsync() { IEnumerable<WeatherForecast> res = _data.AsQueryable(); return Task.FromResult(res.ToArray()); } } -
同時修改 FetchData.razor,因為
WeatherForecastService.GetForecastAsync()洗掉了startDate入參,protected override async Task OnInitializedAsync() { await Task.Delay(1000); // 模擬真實環境,觸發Loading效果 forecasts = await ForecastService.GetForecastAsync(); // here }
添加查詢輸入框和搜索按鈕
-
在 FetchData.razor 頁面中的
<p>標簽下添加以下代碼<MRow > <MCol Cols="12"> <MTextField @bind-Value="https://www.cnblogs.com/capdiem/p/summary" Dense HideDetails="@("auto")" Label="Summary" Outlined> </MTextField> </MCol> <MCol Cols="12" > <MSpacer></MSpacer> <MButton Color="primary" OnClick="OnSearch">搜索</MButton> </MCol> </MRow> @code { private string summary; private async Task OnSearch() { forecasts = await ForecastService.GetForecastAsync(summary); } }-
Line 3,17
定義了一個
string型別的名為summary的變數,雙向系結給了MTextField組件,MTextFiled除了@bind-Value屬性用于設定雙向系結,其他屬性的含義請閱讀 檔案, -
Line 12
定義了一個搜索按鈕,用于觸發查詢,
-
-
修改
WeatherForecastService.GetForecastAsync方法,增加summary入參,并支持查詢,public Task<WeatherForecast[]> GetForecastAsync(string? summary = null) { IEnumerable<WeatherForecast> res = _data.AsQueryable(); if (!string.IsNullOrEmpty(summary)) { res = res.Where(item => item.Summary.Contains(summary)); } return Task.FromResult(res.ToArray()); }

支持多個查詢條件和重置
現在讓我們再添加一個高溫預警的選擇框來查詢不同高溫預警狀態的資料,
更新 WeatherForecastService 以支持根據高溫預警篩選資料
public Task<WeatherForecast[]> GetForecastAsync(string? summary = null, WarningSigns? warningSigns = null)
{
IEnumerable<WeatherForecast> res = _data.AsQueryable();
if (!string.IsNullOrEmpty(summary))
{
res = res.Where(item => item.Summary.Contains(summary));
}
if (warningSigns.HasValue)
{
res = warningSigns switch
{
WarningSigns.Yellow => res.Where(item => item.TemperatureC >= 35 && item.TemperatureC < 37),
WarningSigns.Orange => res.Where(item => item.TemperatureC >= 37 && item.TemperatureC < 39),
WarningSigns.Red => res.Where(item => item.TemperatureC >= 39),
_ => res
};
}
return Task.FromResult(res.ToArray());
}
增加高溫預警選擇框
-
在 Data 目錄下添加名為
WarningSigns的列舉,public enum WarningSigns { [Description("高溫黃色預警 35℃")] Yellow = 1, [Description("高溫橙色預警 37℃")] Orange, [Description("高溫紅色預警 39℃")] Red } -
引入
Masa.Utils.Enums包,此包提供的GetEnumObjectList方法能輕松的將列舉的Description和列舉值用于MSelect組件的Items,dotnet add package Masa.Utils.Enums -
更新 FetchData.razor,
<MRow > <MCol Cols="12" Sm="6"> <MTextField @bind-Value="https://www.cnblogs.com/capdiem/p/@summary" Label="Summary" Dense HideDetails="@("auto")" Outlined> </MTextField> </MCol> <MCol Cols="12" Sm="6"> <MSelect @bind-Value="https://www.cnblogs.com/capdiem/p/warningSigns" Items="@(Enum<WarningSigns>.GetEnumObjectList<WarningSigns>())" ItemText="item => item.Name" ItemValue="https://www.cnblogs.com/capdiem/p/item => item.Value" TValue="https://www.cnblogs.com/capdiem/p/WarningSigns?" TItem="EnumObject<WarningSigns>" TItemValue="https://www.cnblogs.com/capdiem/p/WarningSigns" Label="高溫警告" Clearable Dense HideDetails="@("auto")" Outlined> </MSelect> </MCol> <MCol Cols="12" > <MSpacer></MSpacer> <MButton OnClick="OnReset">重置</MButton> <MButton Color="primary" OnClick="OnSearch">搜索</MButton> </MCol> </MRow> @code { private WarningSigns? warningSigns; private Task OnReset() { summary = null; warningSigns = null; return OnSearch(); } private async Task OnSearch() { forecasts = await ForecastService.GetForecastAsync(summary, warningSigns); } }- Line 2,10
通過設定
Sm="6"可以讓螢屏尺寸大于768px時一行占兩個MCol,實作MTextField和MSelect并排顯示,- Line 11-23,33,44
第33行定義
warningSigns變數用于接收MSelect選中的值,當然也可以通過設定值更新MSelect選中的值,只要設定了@bind-Value雙向系結就行,就像第11行那樣,第12行使用了Masa.Utils.Enums提供的方法,回傳了一個包含Name(Description)和Value(列舉值)的串列,賦值給了MSelect.Items,第44行將warningSigns的傳給查詢介面,- Line 27,35-40
此處定義了一個重置按鈕,用于清空所有查詢輸入框的內容并重繪表格,

支持鍵入回車或選擇后觸發查詢
后來測驗小姐姐說你這太難用了,回車不能觸發搜索,選擇完也不能觸發搜索,好吧好吧,我們現在加上,
鍵入回車后觸發
原理即捕捉 OnKeyDown 事件是否點擊了 Enter 鍵,
<MTextField @bind-Value="https://www.cnblogs.com/capdiem/p/@summary"
OnKeyDown="HandleOnKeyDown"
Label="Summary"
Dense
HideDetails="@("auto")"
Outlined>
</MTextField>
@code {
private async Task HandleOnKeyDown(KeyboardEventArgs args)
{
if (args.Code == "Enter")
{
// 等待156毫秒,預防輸入的值在更新到變數之前按下Enter鍵
await Task.Delay(156);
await OnSearch();
}
}
}
-
Line 2
將
HandleOnKeyDown系結到MTextField的OnKeyDown事件, -
Line 10-17
通過判斷
KeyboardEventArgs的Code值是否為Enter來觸發搜索,第14行等待156毫秒是為了等待summary的值已經是輸入過后的值,
選擇后觸發查詢
<MSelect @bind-Value="https://www.cnblogs.com/capdiem/p/warningSigns"
Items="@(Enum<WarningSigns>.GetEnumObjectList<WarningSigns>())"
ItemText="item => item.Name"
ItemValue="https://www.cnblogs.com/capdiem/p/item => item.Value"
TValue="https://www.cnblogs.com/capdiem/p/WarningSigns?"
TItem="EnumObject<WarningSigns>"
TItemValue="https://www.cnblogs.com/capdiem/p/WarningSigns"
Label="高溫警告"
OnSelectedItemUpdate="OnSearch"
Clearable
Dense
HideDetails="@("auto")"
Outlined>
</MSelect>
-
Line 9
當選擇項更新時(
OnSelectedItemUpdate)直接呼叫OnSearch方法,觸發查詢,此處不用像上面處理OnKeyDown那樣等待156毫秒,因為OnSelectedItemUpdate是在warningSigns更新后觸發的,
點擊清空圖示觸發查詢
很簡單,只要給 MTextField 和 MSelect 組件添加以下屬性:
Clearable
OnClearClick="OnSearch"

加點Loading影片可好?
好!
<MButton Color="primary"
Loading="searching"
OnClick="HandleOnSearch">
搜索
</MButton>
...
<MDataTable Headers="_headers" Items="forecasts"
Loading="loading"
ItemsPerPage="5" >
...
@code {
private bool loading;
private bool searching;
private async Task HandleOnSearch()
{
searching = true;
await OnSearch();
searching = false;
}
private async Task OnSearch()
{
loading = true;
await Task.Delay(1000);
forecasts = await ForecastService.GetForecastAsync(summary, warningSigns);
loading = false;
}
}
-
Line 2-3,15,17-24
新增
searching變數用于控制搜索按鈕的Loading狀態,同時新增了HandleOnSearch代替原來的OnSearch是為了單獨控制點擊搜索按鈕的影片, -
Line 9,14,28,33
新增
loading變數用于控制MDataTable的Loading狀態,OnSearch方法塊中在介面請求前后設定loading的值,

表的行操作和自定義列樣式
因為篇幅限制,我就不一一把代碼貼出來了,具體代碼請查閱 原始碼 接下來我將針對Table寫一些常見的代碼,如行操作和自定義列樣式,

封裝組件和技巧
我本應該用這節分享的內容將上面的例子重構的程序寫出來,但感徑訓使得本文太冗長,重構后的代碼我也會上傳到 Github 上,
封裝組件
試想一下,當你被分配到好幾個模塊,每個模塊都有至少一個查詢表格頁,你會如何開發?你大概會說復制最合適的代碼檔案,然后重命名檔案名,重命名相應的變數,修修改改就完行了,當然這是一個方法,但不優雅,那優雅的方式是什么,是封裝,我有段時間在全職開發 MASA.Blazor 組件庫,后面因為業務需求分配到了IoT專案幫助Blazor后臺系統的研發和 MASA.Blazor 的實踐,在開發IoT專案時,經常會看見相同的代碼分布在相同的類中,我試著優化重構這些代碼,并從查詢表格頁中抽離封裝了以下幾個組件:
- Filters:接收
OnSearch引數代理查詢,通過context提供onEnter和onSearch方法供單個查詢組件使用, - PageHeader:一個標準的頁頭,包括了標題、副標題、搜索按鈕,并提供
Filters組件的能力, - Actions:提供一組操作按鈕,默認展示前兩個,后面的按鈕會移動到
MMenu中顯示, - BlockText:將相同型別的兩個資料并列顯示,
- ColorChip:提供有限的顏色串列生成帶淺色字體的
MChip, - CopyableText:在文本后提供可以復制內容的圖示按鈕,
- DateTimePicker:提高帶時分秒選擇器的彈出層時間選擇器,
- EllipsisText:根據父級盒子的寬度自動截斷文本,
- GenericColumnRender:渲染DateTime、列舉、bool和其他型別值,可以用于
MDataTable的ItemColContent和Definitions的DetailContent,
PageHeader組件作為 MASA.Blazor 預置組件的一部分已經發布,其他提及的組件還沒有并入 MASA.Blazor 主庫,如果你想要使用或參考,可以訪問 MASA.Blazor.Experimental.Components,關于預置組件和實驗性組件的詳細介紹和使用的文章,后面會由其他同事撰寫和發布,請大家帶多多關注!
MASA.Blazor.Experimental.Components 是一個實驗性組件庫,這意味著該庫的API和功能可能會被重新設計,不過隨著實驗性組件的功能不斷完善和穩定,會隨著 MASA.Blazor 版本的更新而并入主庫,
技巧
善用基類
Blazor的組件其實也是一個類,它默認繼承自 ComponentBase 并提供了許多虛擬方法,我們可以重寫它們來影回應用程式的行為,而這些方法通過繼承機制給所有Blazor組件使用,
在實際開發中,我會發現幾乎每個頁面都會注入 NavigationManager、IJsRunTime 和其他可能存在的業務服務,或者會使用某些共同使用的組件,那我們可以在繼承 ComponentBase 的基礎上再寫一個已經使用了這些服務和組件的基類,
按架構可以創建專門給 @page 組件用的 PageComponentBase 和單純封裝功能的 PureComponentBase,
按業務分類就得看情況了,因為業務更加具體,基類里通常會有注入 HttpClient 或者同型別業務服務,以及任何共同使用的代碼,
SetParametersAsync
SetParametersAsync sets parameters supplied by the component's parent in the render tree or from route parameters.
只需知道每當父級呈現時,都會執行此方法,這意味著它是指定默認引數值的正確位置,
拿前面的例子來說,在使用 MTextField 和 MSelect 時都會設定以下代碼來維持相同的外觀和行為:
Clearable
Dense
HideDetails="@("auto")"
Outlined
那么與其每次都要寫一遍,不如利用 SetParametersAsync 的特性把這些默認引數提前設定:
public class DefaultTextField<TValue> : MTextField<TValue>
{
public override async Task SetParametersAsync(ParameterView parameters)
{
Clearable = true;
Dense = true;
HideDetails = "auto";
Outlined = true;
await base.SetParametersAsync(parameters);
}
}
public class DefaultSelect<TItem, TItemValue, TValue> : MSelect<TItem, TItemValue, TValue>
{
public override async Task SetParametersAsync(ParameterView parameters)
{
Clearable = true;
Dense = true;
HideDetails = "auto";
Outlined = true;
await base.SetParametersAsync(parameters);
}
}
<DefaultTextField @bind-Value="https://www.cnblogs.com/capdiem/p/@summary"
OnKeyDown="@context.onEnter"
OnClearClick="@context.onSearch"
Label="Summary">
</DefaultTextField>
<DefaultSelect @bind-Value="https://www.cnblogs.com/capdiem/p/warningSigns"
Items="@(Enum<WarningSigns>.GetEnumObjectList<WarningSigns>())"
ItemText="item => item.Name"
ItemValue="https://www.cnblogs.com/capdiem/p/item => item.Value"
TValue="https://www.cnblogs.com/capdiem/p/WarningSigns?"
TItem="EnumObject<WarningSigns>"
TItemValue="https://www.cnblogs.com/capdiem/p/WarningSigns"
Label="高溫警告"
OnSelectedItemUpdate="@context.onSearch"
OnClearClick="@context.onSearch">
</DefaultSelect>

未來的計劃
未來我們團隊將繼續優化各個組件的性能,完成缺失的組件,解決BUG問題,完善檔案等,另外,我們也計劃出Blazor相關的教程和分享文章,敬請期待,
感謝閱讀!
資源
- 原始碼
- ?https://github.com/capdiem/MasaBlazorStandardTablePage
- 參考
- https://blazor.masastack.com/
- https://github.com/BlazorComponent/Masa.Blazor
- https://github.com/capdiem/MASA.Blazor.Experimental.Components
開源地址
MASA.BuildingBlocks:https://github.com/masastack/MASA.BuildingBlocks
MASA.Contrib:https://github.com/masastack/MASA.Contrib
MASA.Utils:https://github.com/masastack/MASA.Utils
MASA.EShop:https://github.com/masalabs/MASA.EShop
MASA.Blazor:https://github.com/BlazorComponent/MASA.Blazor
如果你對我們的 MASA Framework 感興趣,無論是代碼貢獻、使用、提 Issue,歡迎聯系我們

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/440500.html
標籤:ASP.NET
上一篇:在ggplot中給出特定的中斷
下一篇:C#-2 C#程式
