SiamCafe · Blog
C# Blazor กับ Low Code No Code — วิธีใช้ Blazor
บทความ

C# Blazor กับ Low Code No Code — วิธีใช้ Blazor

เผยแพร่ 28 พฤษภาคม 2569

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&lt;string&gt; 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&lt;DynamicField&gt; Fields { get; set; } = new();

public string SubmitUrl { get; set; } = "";

public string SubmitMethod { get; set; } = "POST";

}

FormRenderer.razor — Render Dynamic Form

@using MyBlazorApp.LowCode

&lt;EditForm Model="_formData" OnValidSubmit="OnSubmit"&gt;

&lt;h4&gt;@Definition.Title&lt;/h4&gt;

&lt;p&gt;@Definition.Description&lt;/p&gt;

@foreach (var field in Definition.Fields)

{

&lt;div class="mb-3"&gt;

&lt;label class="form-label"&gt;@field.Label&lt;/label&gt;

@switch (field.Type)

{

case "text":

case "email":

case "number":

&lt;input type="@field.Type"

class="form-control"

placeholder="@field.Placeholder"

@bind="_formData[field.Name]"

required="@field.Required" /&gt;

break;

case "textarea":

&lt;textarea class="form-control"

rows="4"

@bind="_formData[field.Name]"&gt;&lt;/textarea&gt;

break;

case "select":

&lt;select class="form-select" @bind="_formData[field.Name]"&gt;

&lt;option value=""&gt;-- Select --&lt;/option&gt;

@foreach (var opt in field.Options)

{

&lt;option value="@opt"&gt;@opt&lt;/option&gt;

}

&lt;/select&gt;

break;

case "checkbox":

&lt;input type="checkbox" class="form-check-input"

@bind="_formData[field.Name]" /&gt;

break;

}

&lt;/div&gt;

}

&lt;button type="submit" class="btn btn-primary"&gt;Submit&lt;/button&gt;

&lt;/EditForm&gt;

ตัวอย่าง Form Definition (JSON)

var contactForm = new FormDefinition

{

Title = "Contact Form",

Description = "กรอกข้อมูลติดต่อ",

SubmitUrl = "/api/contacts",

Fields = new List&lt;DynamicField&gt;

{

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&lt;T&gt; — Generic Data Table Component

@typeparam T

&lt;div class="table-responsive"&gt;

@if (ShowSearch)

{

&lt;input type="text" class="form-control mb-3"

placeholder="Search..."

@bind-value="SearchText"

@bind-value:event="oninput" /&gt;

}

&lt;table class="table table-hover"&gt;

&lt;thead&gt;

&lt;tr&gt;

@foreach (var col in Columns)

{

&lt;th style="cursor:pointer"

@onclick="() =&gt; Sort(col.Field)"&gt;

@col.Title

@if (_sortField == col.Field)

{

&lt;span&gt;@(_sortAsc ? "▲" : "▼")&lt;/span&gt;

}

&lt;/th&gt;

}

&lt;/tr&gt;

&lt;/thead&gt;

&lt;tbody&gt;

@foreach (var item in PagedItems)

{

&lt;tr @onclick="() =&gt; OnRowClick.InvokeAsync(item)"

style="cursor:pointer"&gt;

@RowTemplate(item)

&lt;/tr&gt;

}

&lt;/tbody&gt;

&lt;/table&gt;

&lt;nav&gt;

&lt;ul class="pagination"&gt;

@for (var i = 1; i &lt;= TotalPages; i++)

{

var page = i;

&lt;li class="page-item @(page == CurrentPage ? "active" : "")"&gt;

&lt;a class="page-link" @onclick="() =&gt; GoToPage(page)"&gt;@page&lt;/a&gt;

&lt;/li&gt;

}

&lt;/ul&gt;

&lt;/nav&gt;

&lt;/div&gt;

@code {

[Parameter] public List&lt;T&gt; Items { get; set; } = new();

[Parameter] public List&lt;ColumnDef&gt; Columns { get; set; } = new();

[Parameter] public RenderFragment&lt;T&gt; RowTemplate { get; set; }

[Parameter] public EventCallback&lt;T&gt; 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 =&gt; (int)Math.Ceiling(Items.Count / (double)PageSize);

private List&lt;T&gt; PagedItems =&gt; Items

.Skip((CurrentPage - 1) * PageSize)

.Take(PageSize).ToList();

private void GoToPage(int page) =&gt; 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;

}

ใช้งาน

&lt;DataTable Items="users" Columns="columns" PageSize="20"&gt;

&lt;RowTemplate Context="user"&gt;

&lt;td&gt;@user.Name&lt;/td&gt;

&lt;td&gt;@user.Email&lt;/td&gt;

&lt;td&gt;@user.Role&lt;/td&gt;

&lt;/RowTemplate&gt;

&lt;/DataTable&gt;

Console.WriteLine("Blazor Component Library:");

Console.WriteLine(" DataTable&lt;T&gt; — 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