實(shí)現(xiàn)簡單的Blazor低代碼(低代碼api)
實(shí)現(xiàn)簡單的Blazor低代碼
本篇博客只實(shí)現(xiàn)基本的低代碼,比如新增組件,動態(tài)修改組件參數(shù)
創(chuàng)建項(xiàng)目
首先創(chuàng)建一個(gè)空的Blazor Server
,并且命名LowCode.Web
實(shí)現(xiàn)我們還需要引用一個(gè)Blazor
組件庫,由于作者用Masa Blazor
比較多所以使用Masa Blazor
安裝Masa Blazor
將Masa Blazor
添加到項(xiàng)目依賴中
<ItemGroup>
<PackageReference include=\"Masa.Blazor\" Version=\"1.0.0-preview.3\" />
</ItemGroup>
修改Program.cs
文件 增加了builder.Services.AddMasaBlazor;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages;
builder.Services.AddServerSideBlazor;
builder.Services.AddMasaBlazor;
var app = builder.Build;
if (!app.Environment.IsDevelopment)
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/AspNetcore-hsts.
app.UseHsts;
}
app.UseHttpsRedirection;
app.UseStaticFiles;
app.UseRouting;
app.MapBlazorHub;
app.MapFallbackToPage(\"/_Host\");
app.Run;
打開_Imports.razor
添加以下代碼
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.JSInterop
@using LowCode.Web
@using Masa.Blazor
@using BlazorComponent
@using LowCode.Web.Components
修改Pages_Host.cshtml
,添加以下代碼
@page \"/\"
@using Microsoft.AspNetCore.Components.Web
@namespace LowCode.Web.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<!DOCTYPE html>
<html lang=\"en\">
<head>
<meta charset=\"utf-8\"/>
<base href=\"~/\"/>
<link href=\"css/site.css\" rel=\"stylesheet\"/>
<!-- masa blazor css style -->
<link href=\"_content/Masa.Blazor/css/masa-blazor.min.css\" rel=\"stylesheet\"/>
<!--icon file,import need to use-->
<link href=\"https://cdn.masastack.com/npm/@(\"@mdi\")/font@5.x/css/materialdesignicons.min.css\" rel=\"stylesheet\">
<component type=\"typeof(HeadOutlet)\" render-mode=\"ServerPrerendered\"/>
</head>
<body>
<component type=\"typeof(App)\" render-mode=\"ServerPrerendered\"/>
<div id=\"blazor-error-ui\">
<environment include=\"Staging,Production\">
An error has occurred. This application may no longer respond until reloaded.
</environment>
<environment include=\"Development\">
An unhandled Exception has occurred. See browser dev tools for details.
</environment>
<a href=\"\" Class=\"reload\">Reload</a>
<a class=\"dismiss\">??</a>
</div>
<script src=\"_framework/blazor.server.js\"></script>
<!--js(should lay the end of file)-->
<script src=\"_content/BlazorComponent/js/blazor-component.js\"></script>
</body>
</html>
修改MainLayout.razor
文件
@inherits LayoutComponentBase
<MApp>
@Body
</MApp>
這樣就完成安裝了Masa Blazor
然后開始寫實(shí)現(xiàn)
實(shí)現(xiàn)低代碼組件設(shè)計(jì)
創(chuàng)建Components
文件夾
創(chuàng)建渲染組件ComponentRender.razor
,添加以下代碼
@using LowCode.Web.Components.Options
@RenderFragment
@code{
/// <summary>
/// 渲染組件
/// </summary>
[Parameter]
public RenderFragment RenderFragment { get; set; }
/// <summary>
/// 渲染配置
/// </summary>
public GenerateMButtonOptions GenerateMButtonOptions { get; set; }
/// <summary>
/// 渲染動態(tài)代碼
/// </summary>
public string Code { get; set; }
}
定義組件庫模板 DynamicComponentGenerator.razor
,由于cs
文件不能razor
模板,所以創(chuàng)建razor
文件,添加以下代碼,以下代碼我們添加三個(gè)組件模板,
@using LowCode.Web.Components.Options
@code {
public static (RenderFragment, string) GenerateMButton(GenerateMButtonOptions options)
{
// 定義模板默認(rèn)數(shù)據(jù)
RenderFragment template = @<MButton Block=options.block
Height=options.height
Width=options.width
Class=@options.Class
Style=@options.Style
Dark=options.dark
Attributes=options.attributes>@options.childContent</MButton>;
// 模板定義代碼 (存在問題)
var data = $@\"<MButton Block={options.block}
Height={options.height}
Width={options.width}
Class={options.Class}
Style={options.Style}
Dark={options.dark}
Attributes={options.attributes}>{@options.childContent}</MButton>\";
return (builder => { builder.AddContent(0, template); }, data);
}
public static (RenderFragment, string) GenerateMCard(GenerateMButtonOptions options)
{
RenderFragment template = @<MCard Height=options.height
Width=options.width
Class=@options.Class
Style=@options.Style
Dark=options.dark
Attributes=options.attributes>@options.childContent</MCard>;
var data = $@\"<MCard Height={options.height}
Width={options.width}
Class={options.Class}
Style={options.Style}
Dark={options.dark}
Attributes={options.attributes}>{@options.childContent}</MCard>\";
return (builder => { builder.AddContent(0, template); }, data);
}
public static (RenderFragment, string) GenerateMAvatar(GenerateMButtonOptions options)
{
RenderFragment template = @<MAvatar Height=options.height
Width=options.width
Class=@options.Class
Style=@options.Style
Attributes=options.attributes>@options.childContent</MAvatar>;
var data = $@\"<MAvatar Height={options.height}
Width={options.width}
Class={options.Class}
Style={options.Style}
Attributes={options.attributes}>{@options.childContent}</MAvatar>\";
return (builder => { builder.AddContent(0, template); }, data);
}
}
添加ComponentComponentType.cs
組件枚舉
namespace LowCode.Web.Components;
public enum ComponentType
{
MButton,
MCart,
MAvatar
}
添加ComponentComponentLibrary.razor
用于顯示支持的組件
<div style=\"height: 100px\">
<MButton class=\"button\" OnClick=\" => OnClick?.Invoke(ComponentType.MButton)\">
<MIcon>
mdi-card
</MIcon>
按鈕
</MButton>
<MButton class=\"button\" OnClick=\" => OnClick?.Invoke(ComponentType.MCart)\">
<MIcon>mdi-id-card</MIcon>
卡片
</MButton>
<MButton class=\"button\" OnClick=\" => OnClick?.Invoke(ComponentType.MAvatar)\">
<MIcon>mdi-id-card</MIcon>
頭像
</MButton>
</div>
@code{
public delegate void OnClickDelegate(ComponentType type);
[Parameter]
public OnClickDelegate? OnClick { get; set; }
}
<style>
.button {
margin: 5px;
}
</style>
新增ComponentOptionsGenerateMButtonOptions.cs
添加以下代碼 ,添加組件時(shí)的參數(shù)
using BlazorComponent;
using Microsoft.AspNetCore.Components;
namespace LowCode.Web.Components.Options;
public class GenerateMButtonOptions
{
public string? height { get; set; }
public string? width { get; set; }
public bool block { get; set; }
public bool dark { get; set; }
public string Style { get; set; } = string.Empty;
public string Class { get; set; } = string.Empty;
public Dictionary<string, object>? attributes { get; set; } = new;
public RenderFragment? childContent { get; set; }
}
然后修改PagesIndex.razor
,
@page \"/\"
@using LowCode.Web.Components.Options
<MRow NoGutters>
<MCol>
<MCard Class=\"pa-1\"
Outlined
Style=\"height: 100vh\"
tile>
<ComponentLibrary OnClick=\"CreateOnClick\"></ComponentLibrary>
</MCard>
</MCol>
<MCol Order=\"2\"
Cols=\"12\"
Sm=\"6\"
Md=\"8\">
<MCard Class=\"pa-2\"
Outlined
Style=\"height: 100vh\"
tile>
@foreach (var item in Renders)
{
<render @onclick=\" => Id = item.Key\">
@item.Value.RenderFragment
</render>
}
</MCard>
</MCol>
<MCol Order=\"3\">
<MCard Class=\"pa-2\"
Outlined
Style=\"height:100vh\"
tile>
<MCard>
@*TODO:通過反射實(shí)現(xiàn)獲取組件參數(shù)根據(jù)參數(shù)類型顯示指定組件動態(tài)修改參數(shù)*@
@foreach (var item in Renders)
{
var options = item.Value.GenerateMButtonOptions;
if (item.Key == Id)
{
<MTextField @bind-Value=\"options.width\" Label=\"width\"></MTextField>
<MTextField @bind-Value=\"options.height\" Label=\"height\"></MTextField>
<MTextField @bind-Value=\"options.Style\" Label=\"Style\"></MTextField>
<MTextField @bind-Value=\"options.Class\" Label=\"Class\"></MTextField>
<MDivider></MDivider>
<MButton OnClick=\" => AddOptionsAttribute(options.attributes)\" Block>新增擴(kuò)展參數(shù)輸入框</MButton>
@foreach (var e in options.attributes)
{
<MTextarea NoResize Rows=\"1\" Value=\"@e.Key\" ValueChanged=\"(v) => { options.attributes.Remove(e.Key);options.attributes.Add(v,e.Value);}\"></MTextarea>
<MTextarea NoResize Rows=\"1\" Value=\"@options.attributes[e.Key].ToString\" ValueChanged=\"(v)=>options.attributes[e.Key]= v\"></MTextarea>
}
<MButton Block OnClick=\"=>DeleteComponent(item.Key)\">刪除</MButton>
}
}
</MCard>
</MCard>
</MCol>
</MRow>
@code {
public string Id { get; set; }
private Dictionary<string, Render> Renders = new;
private RenderFragment RenderFragment { get; set; }
private void AddOptionsAttribute(Dictionary<string, object> attribute)
{
attribute.Add(\"new\",\"\");
}
private void DeleteComponent(string key)
{
Renders.Remove(key);
}
private void CreateOnClick(ComponentType type)
{
GenerateMButtonOptions options = ;
string code;
switch (type)
{
case ComponentType.MButton:
options = new
{
childContent = @<span>新建的按鈕</span>,
attributes = new Dictionary<string, object>,
width = \"100px\",
};
(RenderFragment, code) = DynamicComponentGenerator.GenerateMButton(options);
Renders.Add(Guid.NewGuid.ToString(\"N\"), new Render { RenderFragment = RenderFragment, GenerateMButtonOptions = options, Code = code });
break;
case ComponentType.MCart:
options = new
{
childContent = @<MButton>多個(gè)按鈕</MButton>,
attributes = new Dictionary<string, object>,
width = \"100px\",
};
(RenderFragment, code) = DynamicComponentGenerator.GenerateMCard(options);
Renders.Add(Guid.NewGuid.ToString(\"N\"), new Render { RenderFragment = RenderFragment, GenerateMButtonOptions = options, Code = code });
break;
case ComponentType.MAvatar:
options = new
{
childContent = @<MImage Src=\"https://cdn.masastack.com/stack/images/website/masa-blazor/jack.png\" Alt=\"Jack\"></MImage>,
attributes = new Dictionary<string, object>,
width = \"100px\",
};
(RenderFragment, code) = DynamicComponentGenerator.GenerateMAvatar(options);
Renders.Add(Guid.NewGuid.ToString(\"N\"), new Render { RenderFragment = RenderFragment, GenerateMButtonOptions = options, Code = code });
break;
default:
throw new ArgumentOutOfRangeException(nameof(type), type, );
}
StateHasChanged;
}
}
這樣就實(shí)現(xiàn)了整個(gè)簡單的低代碼操作,我們可以使用看看效果,簡單了解實(shí)現(xiàn)原理
我們定義了組件的模板,這個(gè)模板是固定的,通過Blazor
提供的雙向綁定實(shí)現(xiàn)動態(tài)修改組件參數(shù),這種方式可能不太適合完整的低代碼,但是也是不錯(cuò)的思路,
這個(gè)項(xiàng)目還處于Demo階段,不知道是否有大佬一塊研究,研究技術(shù)極限,Blazor非常好用,推薦
GitHub項(xiàng)目是MIT開源,希望大佬一塊學(xué)習(xí),促進(jìn)Blazor生態(tài)
來著Token的分享