Blazor adalah framework antarmuka pengembangan web dari Microsoft yang memungkinkan pengembang membangun aplikasi web interaktif menggunakan C# dan teknologi .NET alih-alih menggunakan JavaScript di sisi klien. Blazor memungkinkan pengembang .NET atau C# mampu mengembangkan aplikasi secara full-stack baik di sisi UI/frontend maupun backend hanya dengan 1 bahasa pemrograman C#.  Penjelasan mengenai Blazor dapat dilihat dalam artikel sebelumnya di alamat https://bayuprn.com/2025/09/apa-itu-blazor-di-asp-net-9/

Pada artikel kali ini, kita akan membuat web blazor sederhana dengan menggunakan Fluent-UI Blazor. Fluent-UI Blazor adalah komponen RAD (Rapid application development) buatan tim microsoft yang dapat langsung digunakan untuk mengembangkan aplikasi Blazor. Aplikasi web Blazor sendiri dapat dibuat tanpa komponen tambahan dan hanya bermodalkan HTML5 saja. Dokumentasi lengkap mengenai Fluent-UI Blazor dapat dilihat pada laman https://www.fluentui-blazor.net/CodeSetup . Mengapa menggunakan Fluent-UI Blazor? Hal ini karena Fluent-UI Blazor gratis, tidak berbayar, buatan tim Microsoft, dan sudah berisi komponen-komponen Blazor siap pakai yang dapat digunakan di dalam proyek web. Meski demikian, kalian dapat menggunakan komponen buatan Syncfusion, Devexpress, atau ComponentOne yang tentu berbayar.

Gambar 1: Blazor component yang tersedia dan siap pakai di Fluent-UI Blazor

Mempersiapkan Project

Pertama-tama, kita terlebih dahulu menginstall template fluentblazor. Bedanya template ini dengan template blazor default adalah, pada template ini sudah ada dependency langsung ke library Fluent-UI Blazor. Buka console, lalu ketikkan perintah berikut ini

dotnet new install Microsoft.FluentUI.AspNetCore.Templates
Gambar 2: Console menampilkan perintah dotnet install new template fluentblazor

Buka Visual Studio 2022, untuk pengembangan dapat menggunakan Visual Studio 2022 Community Edition. Pada Start Window, pilih Create a New Project, lalu Pilih template project dengan nama Fluent Blazor Web App.

Gambar 3: Dialog Create a New Project dengan pilihan template blazor

Selanjutnya pada halaman dialog Configure your new project, isi nama project biar seragam dengan nama MyBlazorApp. Pada Interactive Render Mode, pilih Server. Untuk Interactivity Location, pilih Per Page/component. Lalu click tombol Create.

Gambar 4: Dialog “configure your new project” pada blazor

Setelah project berhasil di create, jika kita lihat pada solution explorer window akan nampak Package dependencies sudah mengarah ke nuget package Microsoft.FluentUI.AspNetCore.Components beserta disertai beberapa sample page yang ada pada folder Components. Folder Component ini nantinya akan berisi semua component blazor yang dibuat baik page halaman web maupun reusable control. Seperti halnya view layout, semua layout ditempatkan di dalam folder Layout. Sedangkan page halaman ditempatkan di dalam folder Pages.

Gambar 5: Solution Explorer menampilkan struktur project blazor web app minimal

Untuk membuat aplikasi web, diperlukan database sumber data. Seperti sebelumnya, di sini kita akan menggunakan sample database AdventureWork2022 yang bisa kita unduh dari alamat https://learn.microsoft.com/en-us/sql/samples/adventureworks-install-configure?view=sql-server-ver16&tabs=ssms#download-backup-files .  Penggunaan database AdventureWork2022 dikarenakan lebih sederhana siap pakai dan sudah ada sampel datanya.

Selanjutnya kita perlu membuat EntityFrameworkCore DbContext. Untuk kebutuhan minimum supaya berjalan lancar dan sekaligus bisa men-generate class DbContext, maka kita perlukan nuget berikut ini :

  • Microsoft.EntityFrameworkCore versi 9.0.4 ke atas
  • Microsoft.EntityFrameworkCore.Design versi 9.0.4 ke atas
  • Microsoft.EntityFrameworkCore.Tools versi 9.0.4 ke atas
  • Microsoft.EntityFrameworkCore.SqlServer versi 9.0.4 ke atas

Install package nuget tersebut melalui nuget package manager (Pada Dependencies lalu klik kanan pilih Manage Nuget Packages). Kemudian setelah semua package terinstall, jalankan perintah berikut di package manager console untuk mulai men-generate dbcontext dan entity class.

Scaffold-DbContext -Connection "Data Source=.\MSSQL2022;Initial Catalog=AdventureWork2022;Persist Security Info=True;Integrated Security=true;MultipleActiveResultSets=False;Encrypt=False;TrustServerCertificate=False;Connection Timeout=30;" -Provider "Microsoft.EntityFrameworkCore.SqlServer" -OutputDir "Entities" -ContextDir "Context" -Context "SalesDbContext" -Schemas "Sales"  -DataAnnotations -UseDatabaseNames -Force -Namespace "EF.Entities" -ContextNamespace "EF.AppDbContext"
Gambar 6: DbContext dan Entity Class yang berhasil di generate jika dilihat pada solution explorer window

Sama seperti project Asp.NET Core lainnya, semua hal mengenai service dependency injection dan startup configuration bisa diatur pada class Program.cs. Buka file Program.cs dan pastikan menambahkan service dbcontext melalui builder.Services.AddDbContext(). Pastikan juga service builder.Services.AddFluentUIComponents() sudah ditambahkan. Berikut listing minimal untuk Program.cs

public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);

        // Add services to the container.
        builder.Services.AddRazorComponents()
            .AddInteractiveServerComponents();
        builder.Services.AddFluentUIComponents();
        builder.Services.AddDbContext<EF.AppDbContext.SalesDbContext>();

        var app = builder.Build();

        // Configure the HTTP request pipeline.
        if (!app.Environment.IsDevelopment())
        {
            app.UseExceptionHandler("/Error");
            // 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.UseAntiforgery();

        app.MapStaticAssets();
        app.MapRazorComponents<App>()
            .AddInteractiveServerRenderMode();

        app.Run();
    }
}

Membuat Halaman Blazor

Kita akan membuat halaman yang menampilkan grid berisi order detail, pada bagian atas grid terdapat search text box untuk mencari nama produk.

Kita perlu membuat class model dari hasil query di EntityFramework. Buatlah class OrderDetailVM dan tambahkan di dalam folder Entities. Berikut adalah kode untuk class OrderDetailVM.

 public class OrderDetailVM
 {
     public int SalesOrderID { get; set; }
     public int SalesOrderDetailID { get; set; }
     public string? CarrierTrackingNumber { get; set; }
     public short OrderQty { get; set; }
     public int ProductID { get; set; }
     public string? ProductName { get; set; }
     public string? ProductSize { get; set; }
     public decimal? ProductWeight { get; set; }
     public string? Class { get; set; }
     public string? Color { get; set; }
     public string? ProductNumber { get; set; }
     public short? SafetyStockLevel { get; set; }
     public decimal? StandardCost { get; set; }
     public int? DaysToManufacture { get; set; }
     public int? SpecialOfferID { get; set; }
     public decimal? UnitPrice { get; set; }
     public decimal? UnitPriceDiscount { get; set; }
     public decimal? LineTotal { get; set; }
     public Guid? rowguid { get; set; }
     public DateTime? ModifiedDate { get; set; }
 }

Kemudian pada folder Pages, klik kanan folder tersebut, Pilih Add > Razor Component. Pada Dialog Add New Item yang muncul, pilih item Razor Component, lalu beri nama Order.razor.

Gambar 7: Menu Add Item yang dapat diakses dari context menu solution explorer window
Gambar 8: Dialog Add new Item Razor Component

Setelah file Order.razor berhasil dibuat, buka file tersebut ke editor. Tambahkan baris kode tersebut tepat pada bagian atas file.

@page "/order"
@rendermode InteractiveServer
@using EF.AppDbContext
@using EF.Entities
@using MyBlazorApp.Entities
@using Microsoft.EntityFrameworkCore;
@using Microsoft.EntityFrameworkCore.Query;
@inject IJSRuntime jsRtm
@inject SalesDbContext dbContext

Bagian kode @page “/order” menjelaskan bahwa halaman tersebut dapat diakses dari Url /order.

Bagian kode @rendermode InteractiveServer menjelaskan bahwa render mode menggunakan InteractiveServer, pilihan render mode bisa berupa InteractiveWebAssembly, Static, atau InteractiveServer.

Bagian kode @inject IJSRuntime jsRtm menjelaskan bahwa kita menggunakan dependency injection untuk memperoleh service IJSRuntime yang merupakan js interop service.

Selanjutnya, tepat di bawah kode @inject tersebut, tambahkan kode razor berikut ini. Bagian ini merupakan view html dengan komponen Fluent-UI Blazor di dalamnya.

<h3>Order</h3>

<div class="row col-md-12">
    &nbsp;
</div>
<div class="row col-md-12">
    &nbsp;
</div>
<div class="row col-md-12">
    <div class="col-md-2">
        <b>Search Product Name: </b>
    </div>
    <div class="col-md-4">
        <FluentTextField  Id="txtSearch" InputMode="InputMode.Text" Placeholder="Search..." @bind-Value="@searchText" Style="width:100%;height:40px;" />
    </div>
    <div class="col-md-2">
        <a class="btn btn-success btn-md" onclick="@btnSearchClick">
            <b>Search</b>
        </a>
    </div>
</div>
<div class="row col-md-12">
    &nbsp;
</div>
<h4>Data Grid</h4>
<div style="height: 434px; overflow:auto;" tabindex="-1">
<FluentDataGrid TGridItem="OrderDetailVM" RowSize="DataGridRowSize.Small" Virtualize="true"
                    Items="@DataSource" ItemKey="@(p => p.rowguid)"                
                    GenerateHeader="GenerateHeaderOption.Sticky"
                    DisplayMode="DataGridDisplayMode.Grid" 
                   
                    ResizableColumns="true" ResizeType="DataGridResizeType.Discrete"
                    Style="width: 2000px" AutoFit="true">
    <TemplateColumn Context="datacontext" Title="No." Sortable="false" Align="Align.Center"  >
        
    </TemplateColumn>
    <PropertyColumn Property="@(p => p.SalesOrderDetailID)" Title="Order Detail ID" Sortable="true" Align="Align.Center" />
    <PropertyColumn Property="@(p => p.SalesOrderID)" Title="Order ID" Sortable="true" Align="Align.Center" />
    <PropertyColumn Property="@(p => p.OrderQty)" Title="Order Qty" Sortable="true" Align="Align.End" Format="0" />
    <PropertyColumn Property="@(p => p.ProductName)" Title="Product Name" Sortable="true" Align="Align.Start" />    
    <PropertyColumn Property="@(p => p.ProductSize)" Title="Product Size" Sortable="true" Align="Align.Start" />
    <PropertyColumn Property="@(p => p.ProductWeight)" Title="Product Weight" Sortable="true" Align="Align.End" Format="0.##" />
    <PropertyColumn Property="@(p => p.Color)" Title="Product Color" Sortable="true" Align="Align.Start" />
    <PropertyColumn Property="@(p => p.Class)" Title="Product Class" Sortable="true" Align="Align.Start" />
    <PropertyColumn Property="@(p => p.ProductNumber)" Title="Product Number" Sortable="true" Align="Align.Start" />
    <PropertyColumn Property="@(p => p.SafetyStockLevel)" Title="Safety Stock Level" Sortable="true" Align="Align.End" Format="0.##" />
    <PropertyColumn Property="@(p => p.StandardCost)" Title="Standard Cost" Sortable="true" Align="Align.End" Format="0.##" />
    <PropertyColumn Property="@(p => p.DaysToManufacture)" Title="Days to Manufacture" Sortable="true" Align="Align.End" Format="0" />
    <PropertyColumn Property="@(p => p.UnitPrice)" Title="Unit Price" Sortable="true" Align="Align.End" Format="0.##" />
    <PropertyColumn Property="@(p => p.UnitPriceDiscount)" Title="Discount" Sortable="true" Align="Align.End" Format="0.##" />
    <PropertyColumn Property="@(p => p.LineTotal)" Title="Total" Sortable="true" Align="Align.End" Format="0.##" />
</FluentDataGrid>
</div>
<div class="row col-md-12">
    <div class="col-md-10">&nbsp;</div>
    <div class="col-md-10">
        <b>Total Rows: @TotalRows</b>
    </div>
</div>

Seperti yang kita lihat, kita menggunakan komponen input FluentTextField dengan event handler onlick @btnSearchClick. Kita juga menggunakan komponen grid FluentDataGrid yang menggunakan datasource dari variable @DataSource.

Jika kita lihat pada kode razor tersebut, kita dapati attribut @bind atau @bind-* , deklarasi ini menandakan data binding two ways antara komponen blazor dengan variable pada code C#.

Deklarasi @bind memungkinkan databinding two ways binding bolak balik dua arah antara komponen dan kode C#. Ketika ada update pada UI, maka variable Property yang ada pada C# akan otomatis terupdate value nya, begitu juga sebaliknya jika variable property di C# value nya berubah akan langsung mengupdate ke UI.

deklarasi @bind-[propertyname] memungkinkan two ways binding ke property tertentu. Sehingga jika @bind-Value maka two way binding terjadi antara property Value dari komponen Blazor dengan variable yang ada pada kode C#.

Penggunaan tanpa @bind hanya akan terjadi one way binding saja.

deklarasi @ref akan memungkinkan membuat reference ke komponen instance / element instance, sehingga kita dapat langsung mengakses semua property atau method yang dimiliki oleh komponen tersebut dari kode C#.

Selanjutnya kita membuat kode C# event handler yang diperlukan untuk menangani semua event , termasuk variable untuk data binding. Masih pada file Order.razor yang sama, tambahkan kode tersebut di bagian paling bawah dari file.

@code {

    private string searchText = string.Empty;
    private IQueryable<OrderDetailVM> DataSource;
    public int TotalRows { 
        get
        {
            return DataSource == null ? 0 : DataSource.Count();
        } 
    }

    public IQueryable<MyBlazorApp.Entities.OrderDetailVM> GetDataSource()
    {
        var sqlQuery = $@"
                                 SELECT o.[SalesOrderID]
                                      ,o.[SalesOrderDetailID]
                                      ,o.[CarrierTrackingNumber]
                                      ,o.[OrderQty]
                                      ,o.[ProductID]
                                      ,p.[Name] as [ProductName]
                                      ,p.Size as [ProductSize]
                                      ,p.Weight as [ProductWeight]
                                      ,p.Color
                                      ,p.Class
                                      ,p.ProductNumber
                                      ,p.SafetyStockLevel
                                      ,p.StandardCost
                                      ,p.DaysToManufacture
                                      ,o.[SpecialOfferID]
                                      ,o.[UnitPrice]
                                      ,o.[UnitPriceDiscount]
                                      ,o.[LineTotal]
                                      ,o.[rowguid]
                                      ,o.[ModifiedDate]
                                  FROM [AdventureWork2022].[Sales].[SalesOrderDetail] o
                                  left  join [AdventureWork2022].[Production].[Product] p ON o.ProductID=p.ProductID
                            ";
        return dbContext.Database.SqlQueryRaw<OrderDetailVM>(sqlQuery).AsQueryable();
    }


    protected override Task OnInitializedAsync()
    {

        DataSource = GetDataSource();       
        return base.OnInitializedAsync();
    }

    protected void btnSearchClick(MouseEventArgs e)
    {
        var iq = GetDataSource();
        if (string.IsNullOrWhiteSpace(searchText))
        {
            jsRtm.InvokeVoidAsync("window.alert", "Kriteria Pencarian belum diisi !");
        }
        else
        {
            var search = searchText.ToLower();
           iq =   iq.Where(vw => vw.ProductName != null && vw.ProductName.ToLower().Contains(search)).AsQueryable();
        }        
        DataSource = iq;
        StateHasChanged();
    }
}

Bagian override OnInitializedAsync lebih sering digunakan untuk inisialisasi variabel atau datasource sebelum ditampilkan ke browser. Kemudian btnSearchClick merupakan method untuk event handler ketika tombol di click. Pada kode tersebut di atas, kita tidak membuat new instance sendiri dari dbcontext. DbContext kita peroleh dari dependency injection yang dideklarasikan melalui @inject di bagian atas halaman. Hal ini untuk mencegah db context terdispose saat masih digunakan atau belum terdispose ketika selesai digunakan.

Pada kode tersebut perhatikan bagian jsRtm.InvokeVoidAsync. Kode tersebut memanggil service JS Runtime Interop untuk menjalankan fungsi js di browser. Dengan JS Runtime Interop , kita bisa set value atau mengambil return value dari function yang ada di javascript untuk diproses di kode C#.

Terakhir , pada folder Layout, buka file NavMenu.razor untuk menambahkan menu secara manual. Kita bisa menambahkannya dengan looping data menu dari database, namun saat ini kita cukup menambahkan baris secara menual. Berikut listing kode untuk menu My Sales Order yang mengarah ke url /order.

@rendermode InteractiveServer

<div class="navmenu">
    <input type="checkbox" title="Menu expand/collapse toggle" id="navmenu-toggle" class="navmenu-icon" />
    <label for="navmenu-toggle" class="navmenu-icon"><FluentIcon Value="@(new Icons.Regular.Size20.Navigation())" Color="Color.Fill" /></label>
    <nav class="sitenav" aria-labelledby="main-menu">
        <FluentNavMenu Id="main-menu" Collapsible="true" Width="250" Title="Navigation menu" @bind-Expanded="expanded" CustomToggle="true">
            <FluentNavLink Href="order" Icon="@(new Icons.Regular.Size20.ShoppingBag())" IconColor="Color.Accent">My Sales Order</FluentNavLink>
            <FluentNavLink Href="/" Match="NavLinkMatch.All" Icon="@(new Icons.Regular.Size20.Home())" IconColor="Color.Accent">Home</FluentNavLink>
            <FluentNavLink Href="counter" Icon="@(new Icons.Regular.Size20.NumberSymbolSquare())" IconColor="Color.Accent">Counter</FluentNavLink>
            <FluentNavLink Href="weather" Icon="@(new Icons.Regular.Size20.WeatherPartlyCloudyDay())" IconColor="Color.Accent">Weather</FluentNavLink>
        </FluentNavMenu>
    </nav>
</div>

@code {
    private bool expanded = true;
}

Jika sudah selesai, jalankan aplikasi dengan klik tombol Debug atau tekan tombol F5. Aplikasi dapat diakses dari alamat https://localhost:7249/order .

Gambar 9: Tampilan halaman Order Detail dengan komponen Fluent-UI Blazor

Kita dapat mencoba filter textbox dan mengklik nya untuk mencari berdasarkan nama produk. Selain itu, data grid mendukung sorting, resize column, dan virtual row.

Gambar 10: Halaman web menampilkan alert jika pencarian tanpa kata kunci
Gambar 11: Data grid terfilter dan mendukung sorting serta column resize

Kesimpulan

  1. Blazor web app mendukung model pengembangan aplikasi event driven yang berbasis event handler
  2. Fluent-UI Blazor merupakan salah satu komponen Blazor gratis yang dapat digunakan untuk pengembangan web
  3. Pengembangan aplikasi web berbasis komponen menggunakan Blazor hampir mirip dengan pengembangan ASP.NET WebForm yang menggunakan event driven, di mana setiap event akan dihandle oleh method event handler. Dan di setiap event handler kita dapat mengakses property dari komponen tersebut. Bedanya pada Blazor, kode untuk html UI dan kode event handler berada pada 1 file razor yang sama dan tidak terpisah (Pada WebForm dipisah ke file Code Behind).
  4. User experience dari pengembangan menggunakan blazor sangat mirip dengan ASP.NET WebForm yang menggunakan update panel Ajax Extension, akan tetapi performa dari Blazor lebih cepat.

Download Full Kode Listing dan Source Code di Sini

,


Leave a Reply

Your email address will not be published. Required fields are marked *