在现代Web开发中,JavaScript长期以来一直是前端开发的主导语言。然而,随着Microsoft推出的Blazor技术,开发者们现在有了一个新的选择。Blazor为Web开发者提供了一个强大而有用的工具,使得它成为JavaScript及其众多扩展框架(如Angular、React等)的真正替代品。
在Pro Coders工作期间,从Blazor的预览版本开始使用,并尝试了许多令人惊叹的功能,主要是基于SignalR构建的Blazor Server-Side。今天,将分享一种技术,它同样适用于客户端Blazor WebAssembly。
众所周知,使用Razor页面来定义Blazor UI,这项技术已经存在了大约5年,假设对它已经很熟悉,因此不会在这里深入讨论Razor。
如果在开发时不知道页面应该是什么样子,内容和页面控件应该根据用户或网站管理员可以动态更改的数据来显示,那么需要动态生成页面。
让以一个真实生活中的例子来讨论——内容管理系统,其中网站管理员可以决定用户在用户资料页面上可以填写哪些字段。让制定一个将在今天实现的用户故事。
根据从服务接收到的控件列表生成UI页面。
支持两种类型的控件:TextEdit和DateEdit。
控件列表具有用于UI生成的属性:Label、Type、Required。
让创建一个新的Blazor项目并保持简单。打开Visual Studio 2019,点击“创建新项目”,然后找到Blazor模板并点击“下一步”:
输入项目名称,在下一页选择“Blazor Server App”,然后点击“创建”:
将看到一个为创建的新解决方案,它将包含Visual Studio模板为学习目的添加的几个页面。可以通过点击播放按钮(绿色三角形)来构建并运行解决方案,以在浏览器中查看创建的应用程序。
更喜欢从定义模型开始,所以创建一个新类ControlDetails.cs并将其放入以下代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace DemoDynamicContent
{
public class ControlDetails
{
public string Type { get; set; }
public string Label { get; set; }
public bool IsRequired { get; set; }
}
}
现在可以创建一个服务类,目前它将返回一些测试数据,所以创建ControlService.cs并添加此代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace DemoDynamicContent
{
public class ControlService
{
public List GetControls()
{
var result = new List();
result.Add(new ControlDetails { Type = "TextEdit", Label = "First Name", IsRequired = true });
result.Add(new ControlDetails { Type = "TextEdit", Label = "Last Name", IsRequired = true });
result.Add(new ControlDetails { Type = "DateEdit", Label = "Birth Date", IsRequired = false });
return result;
}
}
}
在这段代码中,指定了希望在动态页面上显示的三个控件:FirstName、LastName和BirthDate。
需要做的最后一件事是将服务注册为依赖注入,所以简单地打开Startup.cs,定位到ConfigureServices(IServiceCollection services)方法,并在底部添加一行代码,使其看起来像这样:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSingleton();
// added line for ControlService
services.AddSingleton();
}
让重用Counter.razor页面(已经由Visual Studio模板创建)以简化操作。需要删除除了第一行以外的所有行,并开始添加自己的代码,首先使用依赖注入来注入服务:
@page "/counter"
@inject ControlService _controlService
现在需要执行_controlService并遍历返回的控件列表。
@page "/counter"
@inject ControlService _controlService
@foreach (var control in _controlService.GetControls())
{
}
对于每个控件,希望显示一个标记为*的标签,如果它是必需的控件:
@page "/counter"
@inject ControlService _controlService
@foreach (var control in ControlList)
{
@if (control.IsRequired)
{
@(control.Label)*
}
else
{
@control.Label
}
@switch (control.Type)
{
case "TextEdit":
break;
case "DateEdit":
break;
}
}
@code {
private List ControlList;
private Dictionary Values;
protected override async Task OnInitializedAsync()
{
ControlList = _controlService.GetControls();
Values = ControlList.ToDictionary(c => c.Label, c => "");
}
void ValueChanged(ChangeEventArgs a, string label)
{
Values[label] = a.Value.ToString();
}
string GetValue(string label)
{
return Values[label];
}
private void OnClick(MouseEventArgs e)
{
// send your Values
}
}
现在可以编译并运行解决方案。在出现的浏览器窗口中,点击Counter菜单项以查看结果:
将这个想法进一步扩展,将需要将生成的razor控件绑定到razor页面中的属性,并将其存储在例如Dictionary中,其中Key是Label,Value是razor控件的值。
在这里,添加了@code部分,它有服务执行逻辑以及绑定到控件的所有属性和事件。绑定在两个方向上工作。
结果代码将如下所示:
@page "/counter"
@inject ControlService _controlService
@foreach (var control in ControlList)
{
@if (control.IsRequired)
{
@(control.Label)*
}
else
{
@control.Label
}
@switch (control.Type)
{
case "TextEdit":
break;
case "DateEdit":
break;
}
}
@code {
private List ControlList;
private Dictionary Values;
protected override async Task OnInitializedAsync()
{
ControlList = _controlService.GetControls();
Values = ControlList.ToDictionary(c => c.Label, c => "");
}
void ValueChanged(ChangeEventArgs a, string label)
{
Values[label] = a.Value.ToString();
}
string GetValue(string label)
{
return Values[label];
}
private void OnClick(MouseEventArgs e)
{
// send your Values
}
}
如果现在运行解决方案,在OnClick方法中设置一个断点,然后在页面控件中输入值并点击Submit按钮,将在底部的监视面板中看到输入的值:
将输入的值存储在Dictionary中,现在可以将其提供给一个服务,该服务将值保存到数据库中。
实现了一个使用从服务接收的数据生成控件的UI页面。数据控制呈现。提供存储在数据库中的数据,向用户呈现内容。如果想要呈现略有不同的内容——只需要更改数据库中的数据,用户就会看到变化,无需重新编译或部署。
但这个解决方案有一个小限制——它只支持在razor页面上的switch语句中指定的控件。每当需要显示一个在switch语句中未指定的控件时,需要扩展它,添加带有新控件的代码并重新编译解决方案。