C# Blazor กับ Low Code No Code — วิธีใช้ Blazor
Blazor Web Framework
Blazor เป็น Web Framework จาก Microsoft ที่ให้เขียน Web App ด้วย C# แทน JavaScript มี Component-based Architecture เหมือน React/Vue แต่ใช้ C# และ Razor Syntax รองรับทั้ง Server-side และ Client-side (WebAssembly)
Low Code/No Code เป็นแนวคิดที่ให้สร้าง Application ด้วยการลาก Drop Components เขียน Code น้อยที่สุดหรือไม่ต้องเขียนเลย Blazor สามารถสร้าง Low Code Platform ได้ด้วย Dynamic Components และ Drag-and-Drop
Blazor Application Development
// === Blazor Web App ===
// dotnet new blazor -n MyBlazorApp --interactivity Auto
// cd MyBlazorApp
// === Components/UserList.razor ===
@page "/users"
@using MyBlazorApp.Models
@using MyBlazorApp.Services
@inject IUserService UserService
@inject NavigationManager Navigation
<PageTitle>Users</PageTitle>
<h3>User Management</h3>
@if (_loading)
{
<div class="spinner-border" role="status">
<span class="visually-hidden">Loading...</span>
</div>
}
else
{
<div class="mb-3">
<input type="text" class="form-control"
placeholder="Search users..."
@bind-value="_searchText"
@bind-value:event="oninput"
@onkeyup="OnSearch" />
</div>
<table class="table table-striped">
<thead>
<tr>
<th @onclick='() => SortBy("Name")'>Name</th>
<th @onclick='() => SortBy("Email")'>Email</th>
<th>Role</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@foreach (var user in _filteredUsers)
{
<tr>
<td>@user.Name</td>
<td>@user.Email</td>
<td><span class="badge bg-primary">@user.Role</span></td>
<td>
<button class="btn btn-sm btn-outline-primary"
@onclick="() => EditUser(user.Id)">Edit</button>
<button class="btn btn-sm btn-outline-danger"
@onclick="() => DeleteUser(user.Id)">Delete</button>
</td>
</tr>
}
</tbody>
</table>
<p>Total: @_filteredUsers.Count users</p>
}
@code {
private List<User> _users = new();
private List<User> _filteredUsers = new();
private string _searchText = "";
private bool _loading = true;
private string _sortField = "Name";
private bool _sortAsc = true;
protected override async Task OnInitializedAsync()
{
_users = await UserService.GetUsersAsync();
_filteredUsers = _users;
_loading = false;
}
private void OnSearch()
{
_filteredUsers = string.IsNullOrEmpty(_searchText)
? _users
: _users.Where(u =>
u.Name.Contains(_searchText, StringComparison.OrdinalIgnoreCase) ||
u.Email.Contains(_searchText, StringComparison.OrdinalIgnoreCase))
.ToList();
}
private void SortBy(string field)
{
if (_sortField == field) _sortAsc = !_sortAsc;
else { _sortField = field; _sortAsc = true; }
_filteredUsers = _sortField switch
{
"Name" => _sortAsc
? _filteredUsers.OrderBy(u => u.Name).ToList()
: _filteredUsers.OrderByDescending(u => u.Name).ToList(),
"Email" => _sortAsc
? _filteredUsers.OrderBy(u => u.Email).ToList()
: _filteredUsers.OrderByDescending(u => u.Email).ToList(),
_ => _filteredUsers,
};
}
private void EditUser(int id) => Navigation.NavigateTo($"/users/edit/{id}");
private async Task DeleteUser(int id)
{
await UserService.DeleteUserAsync(id);
_users = await UserService.GetUsersAsync();
OnSearch();
}
}
Low Code Component Builder
=== Low Code Dynamic Form Builder ===
Components/FormBuilder.razor
DynamicField.cs — Field Definition
namespace MyBlazorApp.LowCode;
public class DynamicField
{
public string Name { get; set; } = "";
public string Label { get; set; } = "";
public string Type { get; set; } = "text"; // text, number, email, select, checkbox, textarea
public bool Required { get; set; }
public string Placeholder { get; set; } = "";
public List<string> Options { get; set; } = new(); // สำหรับ Select
public string DefaultValue { get; set; } = "";
public int? MinLength { get; set; }
public int? MaxLength { get; set; }
public string ValidationMessage { get; set; } = "";
}
public class FormDefinition
{
public string Title { get; set; } = "";
public string Description { get; set; } = "";
public List<DynamicField> Fields { get; set; } = new();
public string SubmitUrl { get; set; } = "";
public string SubmitMethod { get; set; } = "POST";
}
FormRenderer.razor — Render Dynamic Form
@using MyBlazorApp.LowCode
<EditForm Model="_formData" OnValidSubmit="OnSubmit">
<h4>@Definition.Title</h4>
<p>@Definition.Description</p>
@foreach (var field in Definition.Fields)
{
<div class="mb-3">
<label class="form-label">@field.Label</label>
@switch (field.Type)
{
case "text":
case "email":
case "number":
<input type="@field.Type"
class="form-control"
placeholder="@field.Placeholder"
@bind="_formData[field.Name]"
required="@field.Required" />
break;
case "textarea":
<textarea class="form-control"
rows="4"
@bind="_formData[field.Name]"></textarea>
break;
case "select":
<select class="form-select" @bind="_formData[field.Name]">
<option value="">-- Select --</option>
@foreach (var opt in field.Options)
{
<option value="@opt">@opt</option>
}
</select>
break;
case "checkbox":
<input type="checkbox" class="form-check-input"
@bind="_formData[field.Name]" />
break;
}
</div>
}
<button type="submit" class="btn btn-primary">Submit</button>
</EditForm>
ตัวอย่าง Form Definition (JSON)
var contactForm = new FormDefinition
{
Title = "Contact Form",
Description = "กรอกข้อมูลติดต่อ",
SubmitUrl = "/api/contacts",
Fields = new List<DynamicField>
{
new() { Name = "name", Label = "ชื่อ", Type = "text", Required = true },
new() { Name = "email", Label = "Email", Type = "email", Required = true },
new() { Name = "phone", Label = "โทรศัพท์", Type = "text" },
new() { Name = "department", Label = "แผนก", Type = "select",
Options = new() { "Sales", "Support", "Engineering" } },
new() { Name = "message", Label = "ข้อความ", Type = "textarea", Required = true },
new() { Name = "subscribe", Label = "รับข่าวสาร", Type = "checkbox" },
},
};
Console.WriteLine($"Form: {contactForm.Title}");
Console.WriteLine($"Fields: {contactForm.Fields.Count}");
foreach (var f in contactForm.Fields)
Console.WriteLine($" {f.Name} ({f.Type}) {(f.Required ? "*" : "")}");
Blazor Component Library
=== Reusable Blazor Components ===
Components/Shared/DataTable.razor
DataTable<T> — Generic Data Table Component
@typeparam T
<div class="table-responsive">
@if (ShowSearch)
{
<input type="text" class="form-control mb-3"
placeholder="Search..."
@bind-value="SearchText"
@bind-value:event="oninput" />
}
<table class="table table-hover">
<thead>
<tr>
@foreach (var col in Columns)
{
<th style="cursor:pointer"
@onclick="() => Sort(col.Field)">
@col.Title
@if (_sortField == col.Field)
{
<span>@(_sortAsc ? "▲" : "▼")</span>
}
</th>
}
</tr>
</thead>
<tbody>
@foreach (var item in PagedItems)
{
<tr @onclick="() => OnRowClick.InvokeAsync(item)"
style="cursor:pointer">
@RowTemplate(item)
</tr>
}
</tbody>
</table>
<nav>
<ul class="pagination">
@for (var i = 1; i <= TotalPages; i++)
{
var page = i;
<li class="page-item @(page == CurrentPage ? "active" : "")">
<a class="page-link" @onclick="() => GoToPage(page)">@page</a>
</li>
}
</ul>
</nav>
</div>
@code {
[Parameter] public List<T> Items { get; set; } = new();
[Parameter] public List<ColumnDef> Columns { get; set; } = new();
[Parameter] public RenderFragment<T> RowTemplate { get; set; }
[Parameter] public EventCallback<T> OnRowClick { get; set; }
[Parameter] public bool ShowSearch { get; set; } = true;
[Parameter] public int PageSize { get; set; } = 10;
private string SearchText = "";
private int CurrentPage = 1;
private string _sortField = "";
private bool _sortAsc = true;
private int TotalPages => (int)Math.Ceiling(Items.Count / (double)PageSize);
private List<T> PagedItems => Items
.Skip((CurrentPage - 1) * PageSize)
.Take(PageSize).ToList();
private void GoToPage(int page) => CurrentPage = page;
private void Sort(string field) { /* sorting logic */ }
}
ColumnDef.cs
public class ColumnDef
{
public string Field { get; set; } = "";
public string Title { get; set; } = "";
public string Width { get; set; } = "";
public bool Sortable { get; set; } = true;
}
ใช้งาน
<DataTable Items="users" Columns="columns" PageSize="20">
<RowTemplate Context="user">
<td>@user.Name</td>
<td>@user.Email</td>
<td>@user.Role</td>
</RowTemplate>
</DataTable>
Console.WriteLine("Blazor Component Library:");
Console.WriteLine(" DataTable<T> — Generic sortable, searchable, paginated table");
Console.WriteLine(" FormBuilder — Dynamic form from JSON definition");
Console.WriteLine(" DragDrop — Drag and drop UI builder");
Console.WriteLine(" ChartWidget — Chart.js wrapper for Blazor");
Best Practices
- Component Reusability: สร้าง Generic Components ที่ใช้ซ้ำได้ เช่น DataTable<T>, FormField
- State Management: ใช้ Cascading Parameters หรือ Fluxor สำหรับ Global State
- Lazy Loading: ใช้ Lazy Assembly Loading สำหรับ Blazor WASM ลด Initial Load
- Virtualization: ใช้ Virtualize Component สำหรับ List ที่มีข้อมูลมาก
- Error Boundaries: ใช้ ErrorBoundary Component จับ Errors ไม่ให้ App Crash
- Pre-rendering: เปิด Pre-rendering สำหรับ SEO และ Initial Load เร็วขึ้น
Blazor คืออะไร
Web Framework จาก Microsoft เขียน Web App ด้วย C# แทน JavaScript มี Blazor Server ประมวลผลบน Server SignalR และ Blazor WASM รันบน Browser WebAssembly ไม่ต้องเขียน JavaScript