使用MASA Blazor开发查询表格页

 更新时间:2022年04月26日 15:27:41   作者:capdiem  
MASA Blazor 是一个基于 Material Design 设计语言的 Blazor 组件库,dotNET开发者只需或者甚至不需要懂得 javascript 就能开发一个企业级中后台系统,这篇文章主要介绍了使用MASA Blazor开发一个标准的查询表格页,需要的朋友可以参考下

前言

大家好,我是开源项目 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 = 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 Class="pb-3">
    <MCol Cols="12">
        <MTextField @bind-Value="summary"
                    Dense
                    HideDetails="@("auto")"
                    Label="Summary"
                    Outlined>
        </MTextField>
    </MCol>
    <MCol Cols="12" Class="d-flex py-0 pb-3">
        <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 Class="pb-3">
        <MCol Cols="12" Sm="6">
        <MTextField @bind-Value="@summary"
                    Label="Summary"
                    Dense
                    HideDetails="@("auto")"
                    Outlined>
        </MTextField>
    </MCol>
    <MCol Cols="12" Sm="6">
        <MSelect @bind-Value="warningSigns"
                Items="@(Enum<WarningSigns>.GetEnumObjectList<WarningSigns>())"
                ItemText="item => item.Name"
                ItemValue="item => item.Value"
                TValue="WarningSigns?"
                TItem="EnumObject<WarningSigns>"
                TItemValue="WarningSigns"
                Label="高温警告"
                Clearable
                Dense
                HideDetails="@("auto")"
                Outlined>
        </MSelect>
    </MCol>
    <MCol Cols="12" Class="d-flex py-0 pb-3">
        <MSpacer></MSpacer>
        <MButton Class="mr-2" 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 ,实现 MTextFieldMSelect 并排显示。

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="@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 绑定到 MTextFieldOnKeyDown 事件。

Line 10-17

通过判断 KeyboardEventArgsCode 值是否为 Enter 来触发搜索。第14行等待156毫秒是为了等待 summary 的值已经是输入过后的值。

选择后触发查询

<MSelect @bind-Value="warningSigns"
    	 Items="@(Enum<WarningSigns>.GetEnumObjectList<WarningSigns>())"
    	 ItemText="item => item.Name"
    	 ItemValue="item => item.Value"
    	 TValue="WarningSigns?"
    	 TItem="EnumObject<WarningSigns>"
    	 TItemValue="WarningSigns"
    	 Label="高温警告"
    	 OnSelectedItemUpdate="OnSearch"
    	 Clearable
         Dense
         HideDetails="@("auto")"
    	 Outlined>
</MSelect>

Line 9

当选择项更新时(OnSelectedItemUpdate)直接调用 OnSearch 方法,触发查询。此处不用像上面处理 OnKeyDown 那样等待156毫秒,因为 OnSelectedItemUpdate 是在 warningSigns 更新后触发的。

点击清空图标触发查询

很简单,只要给 MTextFieldMSelect 组件添加以下属性:

Clearable
OnClearClick="OnSearch"

加点Loading动画可好?

好!

<MButton Color="primary" 
    	 Loading="searching" 
    	 OnClick="HandleOnSearch">
    搜索
</MButton>
...

<MDataTable Headers="_headers" Items="forecasts" 
            Loading="loading" 
            ItemsPerPage="5" Class="elevation-1">
    
@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 变量用于控制 MDataTableLoading 状态。OnSearch 方法块中在接口请求前后设置 loading 的值。

表的行操作和自定义列样式

因为篇幅限制,我就不一一把代码贴出来了,具体代码请查阅 源码 接下来我将针对Table写一些常见的代码,如行操作和自定义列样式。

封装组件和技巧

我本应该用这节分享的内容将上面的例子重构的过程写出来,但感觉会使得本文太冗长。重构后的代码我也会上传到 Github 上。

封装组件

试想一下,当你被分配到好几个模块,每个模块都有至少一个查询表格页,你会如何开发?你大概会说复制最合适的代码文件,然后重命名文件名,重命名相应的变量,修修改改就完行了。当然这是一个方法,但不优雅。那优雅的方式是什么,是封装。我有段时间在全职开发 MASA.Blazor 组件库,后面因为业务需求分配到了IoT项目帮助Blazor后台系统的研发和 MASA.Blazor 的实践。在开发IoT项目时,经常会看见相同的代码分布在相同的类中,我试着优化重构这些代码,并从查询表格页中抽离封装了以下几个组件:

  • Filters:接收OnSearch参数代理查询,通过context提供onEnteronSearch方法供单个查询组件使用。
  • PageHeader:一个标准的页头,包括了标题、副标题、搜索按钮,并提供Filters组件的能力。
  • Actions:提供一组操作按钮,默认展示前两个,后面的按钮会移动到MMenu中显示。
  • BlockText:将相同类型的两个数据并列显示。ColorChip:提供有限的颜色列表生成带浅色字体的MChip
  • CopyableText:在文本后提供可以复制内容的图标按钮。
  • DateTimePicker:提高带时分秒选择器的弹出层时间选择器。
  • EllipsisText:根据父级盒子的宽度自动截断文本。GenericColumnRender:渲染DateTime、枚举、bool和其他类型值。可以用于MDataTableItemColContentDefinitionsDetailContent

PageHeader组件作为 MASA.Blazor 预置组件的一部分已经发布,其他提及的组件还没有并入 MASA.Blazor 主库。如果你想要使用或参考,可以访问 MASA.Blazor.Experimental.Components。关于预置组件和实验性组件的详细介绍和使用的文章,后面会由其他同事编写和发布,请大家带多多关注!

MASA.Blazor.Experimental.Components 是一个实验性组件库,这意味着该库的API和功能可能会被重新设计。不过随着实验性组件的功能不断完善和稳定,会随着 MASA.Blazor 版本的更新而并入主库。

 

技巧

善用基类

Blazor的组件其实也是一个类,它默认继承自 ComponentBase 并提供了许多虚拟方法,我们可以重写它们来影响应用程序的行为。而这些方法通过继承机制给所有Blazor组件使用。

在实际开发中,我会发现几乎每个页面都会注入 NavigationManagerIJsRunTime 和其他可能存在的业务服务,或者会使用某些共同使用的组件,那我们可以在继承 ComponentBase 的基础上再写一个已经使用了这些服务和组件的基类。

按架构可以创建专门给 @page 组件用的 PageComponentBase 和单纯封装功能的 PureComponentBase
按业务分类就得看情况了,因为业务更加具体,基类里通常会有注入 HttpClient 或者同类型业务服务,以及任何共同使用的代码。

SetParametersAsync

SetParametersAsync sets parameters supplied by the component's parent in the render tree or from route parameters.

只需知道每当父级呈现时,都会执行此方法。这意味着它是指定默认参数值的正确位置。
拿前面的例子来说,在使用 MTextFieldMSelect 时都会设置以下代码来维持相同的外观和行为:

Clearable
Dense
HideDetails="@("auto")"
Outlined

那么与其每次都要写一遍,不如利用 SetParametersAsync 的特性把这些默认参数提前设置:

<DefaultTextField @bind-Value="@summary"
                  OnKeyDown="@context.onEnter"
                  OnClearClick="@context.onSearch"
                  Label="Summary">
</DefaultTextField>

<DefaultSelect @bind-Value="warningSigns"
               Items="@(Enum<WarningSigns>.GetEnumObjectList<WarningSigns>())"
               ItemText="item => item.Name"
               ItemValue="item => item.Value"
               TValue="WarningSigns?"
               TItem="EnumObject<WarningSigns>"
               TItemValue="WarningSigns"
               Label="高温警告"
               OnSelectedItemUpdate="@context.onSearch"
               OnClearClick="@context.onSearch">
</DefaultSelect>
<DefaultTextField @bind-Value="@summary"
                  OnKeyDown="@context.onEnter"
                  OnClearClick="@context.onSearch"
                  Label="Summary">
</DefaultTextField>

<DefaultSelect @bind-Value="warningSigns"
               Items="@(Enum<WarningSigns>.GetEnumObjectList<WarningSigns>())"
               ItemText="item => item.Name"
               ItemValue="item => item.Value"
               TValue="WarningSigns?"
               TItem="EnumObject<WarningSigns>"
               TItemValue="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 Blazor开发一个标准的查询表格页的文章就介绍到这了,更多相关MASA Blazor查询表格页内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 使用postman进行接口自动化测试

    使用postman进行接口自动化测试

    这篇文章介绍了使用postman进行接口自动化测试的方法,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-06-06
  • Git初次使用时的安装配置图文教程

    Git初次使用时的安装配置图文教程

    下面小编就为大家分享一篇Git初次使用时的安装配置图文教程,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2017-11-11
  • Git恢复之前版本的两种方法reset、revert(图文详解)

    Git恢复之前版本的两种方法reset、revert(图文详解)

    这篇文章主要介绍了Git恢复之前版本的两种方法reset、revert(图文详解),文中通过图文示例介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • git-pycharm配置.ignore文件的详细过程

    git-pycharm配置.ignore文件的详细过程

    在开发中经常需要配置提交git的忽略文件,本篇来学习下使用pycharm自动生成.ignore文件,感兴趣的朋友跟随小编一起看看吧
    2023-06-06
  • 从0到1搭建后端架构的演进(MVC,服务拆分,微服务,领域驱动)

    从0到1搭建后端架构的演进(MVC,服务拆分,微服务,领域驱动)

    来腾讯之前在前公司做了3年的后端开发,经历一款SaaS产品从0到10(还没有到100, 哈哈哈)的过程,3年间后端的架构逐步演变,在微服务的实践过程中遇到的问题也越来越多,在这里总结下
    2021-08-08
  • 一文分享如何使用vscode打断点

    一文分享如何使用vscode打断点

    这篇文章主要给大家介绍了关于如何使用vscode打断点的相关资料,最近用vscode进行断点调试的时候总是不顺利,遂自己总结了断点调试的方法,需要的朋友可以参考下
    2023-07-07
  • 详解Google Protobuf简明教程

    详解Google Protobuf简明教程

    这篇文章主要介绍了详解Google Protobuf简明教程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-02-02
  • Fiddler Everywhere 4.0.1的破解及禁止更新的方法(最新推荐)

    Fiddler Everywhere 4.0.1的破解及禁止更新的方法(最新推荐)

    Fiddler Everywhere是个好东西,无论抓包还是调试接口都很好用,只是现在收费了,为了白嫖就决定折腾一下,如果一开始通过设置文件夹权限禁止更新,也会导致进不了使用界面而无法使用,下面说一下具体的解决过程
    2023-11-11
  • 解决Windows磁盘有锁和感叹号方法

    解决Windows磁盘有锁和感叹号方法

    目前在整理自己新电脑的软件,无意间电脑磁盘有锁和感叹号的标志,最后,查询才知道这种现象是微软操作系统自带的BitLocker在作祟,接下来解决这个问题吧
    2007-02-02
  • 推荐四款可视化工具(解决99%的可视化大屏需求)

    推荐四款可视化工具(解决99%的可视化大屏需求)

    这篇文章主要介绍了推荐四款可视化工具(解决99%的可视化大屏需求),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2021-04-04

最新评论