本篇部落格只實現基本的低程式碼,比如新增元件,動態修改元件引數
建立專案
首先建立一個空的Blazor Server
,並且命名LowCode.Web
實現我們還需要引用一個Blazor
元件庫,由於作者用Masa Blazor
比較多所以使用Masa Blazor
安裝Masa Blazor
將Masa Blazor
新增到專案依賴中
<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
然後開始寫實現
實現低程式碼元件設計
建立Components
資料夾
建立渲染元件Component\Render.razor
,新增以下程式碼
@using LowCode.Web.Components.Options
@RenderFragment
@code{
/// <summary>
/// 渲染元件
/// </summary>
[Parameter]
public RenderFragment RenderFragment { get; set; }
/// <summary>
/// 渲染配置
/// </summary>
public GenerateMButtonOptions GenerateMButtonOptions { get; set; }
/// <summary>
/// 渲染動態程式碼
/// </summary>
public string Code { get; set; }
}
定義元件庫模板 DynamicComponentGenerator.razor
,由於cs
檔案不能razor
模板,所以建立razor
檔案,新增以下程式碼,以下程式碼我們新增三個元件模板,
@using LowCode.Web.Components.Options
@code {
public static (RenderFragment, string) GenerateMButton(GenerateMButtonOptions options)
{
// 定義模板預設資料
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);
}
}
新增Component\ComponentType.cs
元件列舉
namespace LowCode.Web.Components;
public enum ComponentType
{
MButton,
MCart,
MAvatar
}
新增Component\ComponentLibrary.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>
新增Component\Options\GenerateMButtonOptions.cs
新增以下程式碼 ,新增元件時的引數
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; }
}
然後修改Pages\Index.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:透過反射實現獲取元件引數根據引數型別顯示指定元件動態修改引數*@
@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>新增擴充套件引數輸入框</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 = null;
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>多個按鈕</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, null);
}
StateHasChanged();
}
}
這樣就實現了整個簡單的低程式碼操作,我們可以使用看看效果,簡單瞭解實現原理
我們定義了元件的模板,這個模板是固定的,透過Blazor
提供的雙向繫結實現動態修改元件引數,這種方式可能不太適合完整的低程式碼,但是也是不錯的思路,
這個專案還處於Demo階段,不知道是否有大佬一塊研究,研究技術極限,Blazor非常好用,推薦
GitHub
專案是MIT開源,希望大佬一塊學習,促進Blazor生態
來著Token的分享