Technology

C# Blazor Low Code No Code

c blazor low code no code
C# Blazor Low Code No Code | SiamCafe Blog
2025-07-10· อ. บอม — SiamCafe.net· 8,668 คำ

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

Blazor คืออะไร

Web Framework จาก Microsoft เขียน Web App ด้วย C# แทน JavaScript มี Blazor Server ประมวลผลบน Server SignalR และ Blazor WASM รันบน Browser WebAssembly ไม่ต้องเขียน JavaScript

Low Code/No Code คืออะไร

Platform สร้าง Application ลาก Drop Components เขียน Code น้อย No Code ไม่ต้องเขียนเลย Visual Builder เหมาะ Business Users ตัวอย่าง Power Apps OutSystems Mendix Bubble

Blazor Server กับ Blazor WASM ต่างกันอย่างไร

Server ประมวลผลบน Server DOM Updates SignalR WebSocket โหลดเร็ว ต้อง Connection WASM รันบน Browser WebAssembly Offline ได้ Initial Load ช้ากว่า ไม่ต้องพึ่ง Server

Blazor เหมาะกับงานแบบไหน

ทีมถนัด C#/.NET ไม่ต้องเรียน JavaScript Enterprise Apps Type Safety Internal Tools Dashboard Admin Panel Forms-heavy ใช้ MudBlazor Radzen Syncfusion

สรุป

Blazor ให้เขียน Web App ด้วย C# ใช้ Component-based Architecture เหมือน React/Vue เมื่อรวมกับ Low Code สร้าง Dynamic Forms จาก JSON Definition Drag-and-Drop UI Builder Generic Components ที่ใช้ซ้ำได้ เหมาะกับ Enterprise Apps ที่ทีมถนัด .NET

📖 บทความที่เกี่ยวข้อง

Cloudflare D1 Low Code No Codeอ่านบทความ → QuestDB Time Series Low Code No Codeอ่านบทความ → MongoDB Change Streams Low Code No Codeอ่านบทความ → Snyk Code Security Low Code No Codeอ่านบทความ → Python Rich Code Review Best Practiceอ่านบทความ →

📚 ดูบทความทั้งหมด →