Parameters
Truyền dữ liệu giữa các component, nhận sự kiện từ component con và tái sử dụng giao diện bằng template.
Mỗi ví dụ phân biệt component cha và con. Thay đổi dữ liệu ở cha, thử nút trong con rồi đối chiếu phần mã của từng component.
01 · Route Parameter — tham số trên URL
Route param là route của một page. Bên dưới chỉ là code sample để xem (không chạy trong page này).
Xem code (route param sample)
@page "/product/{Id:int}"
<h3>Product Id: @Id</h3>
@code {
[Parameter]
public int Id { get; set; }
}
02 · Truyền dữ liệu từ cha xuống con
CHA truyền dữ liệu xuống CON qua attribute.
Xem code
<button class="btn btn-primary" @onclick="() => Count++">Count++</button>
<span><b>Parent Count:</b> @Count</span>
<ParamChild Title="Hello from Parent" Count="@Count" />
@code {
private int Count = 0;
}
<div class="border rounded p-2">
<div><b>ParamChild</b></div>
<div>Title: @Title</div>
<div>Count: @Count</div>
</div>
@code {
[Parameter] public string Title { get; set; } = "";
[Parameter] public int Count { get; set; }
}
03 · Giá trị mặc định và EditorRequired
Parameter có thể có default. Nếu muốn “bắt buộc truyền”, dùng [EditorRequired].
Xem code
<!-- Không truyền Count -->
<ParamChild Title="Chỉ truyền Title (Count sẽ default)" />
<div>Title: @Title</div>
<div>Count: @Count</div>
@code {
[Parameter, EditorRequired]
public string Title { get; set; } = "";
// Default value nếu CHA không truyền
[Parameter]
public int Count { get; set; } = 100;
}
04 · CascadingParameter — dữ liệu dùng chung nhiều cấp
CascadingParameter dùng khi bạn muốn truyền dữ liệu dùng chung từ component cha xuống tất cả component con/cháu mà không cần truyền qua từng cấp.
Component cha “phát” dữ liệu bằng <CascadingValue>,
component con nhận bằng [CascadingParameter].
Xem code
<select class="form-select w-25" @bind="Theme">
<option>Light</option>
<option>Dark</option>
<option>Blue</option>
</select>
<CascadingValue Value="@Theme">
<ParamChildCascading />
</CascadingValue>
@code {
private string Theme = "Light";
}
<div class="border rounded p-2">
<div><b>ParamChildCascading</b></div>
<div>Theme: <b>@Theme</b></div>
</div>
@code {
[CascadingParameter]
public string? Theme { get; set; }
}
05 · Nhận thêm attribute với CaptureUnmatchedValues
CHA truyền thêm class/style/data-*, CON nhận bằng AdditionalAttributes.
Xem code
<ParamChildWithAttrs class="border p-2 rounded"
style="margin-top:8px;"
data-test="abc"
Text="Component nhận attrs" />
<div @attributes="AdditionalAttributes">
<b>ParamChildWithAttrs:</b> @Text
</div>
@code {
[Parameter] public string Text { get; set; } = "";
[Parameter(CaptureUnmatchedValues = true)]
public Dictionary<string, object>? AdditionalAttributes { get; set; }
}
06 · EventCallback — sự kiện từ con lên cha
CON bắn sự kiện lên CHA qua EventCallback.
Last click: none
Xem code
// CHA
<ParamChildEvent OnClicked="HandleChildClicked" />
@code {
private string LastClick = "none";
private void HandleChildClicked()
{
LastClick = $"Child clicked at {DateTime.Now:HH:mm:ss}";
}
}
// CON
<button class="btn btn-outline-success" @onclick="OnClicked"> Child Click Me </button>
@code {
[Parameter] public EventCallback OnClicked { get; set; }
}
// CHA
<ParamChildEvent OnClicked="HandleChildClicked" />
@code {
private void HandleChildClicked()
{
LastClick = $"Child clicked at {DateTime.Now:HH:mm:ss}";
}
}
// CON
<button class="btn btn-outline-success" @onclick="HandleClick"> Child Click Me </button>
@code {
[Parameter] public EventCallback OnClicked { get; set; }
private async Task HandleClick()
{
// CON xử lý trước khi gọi CHA
await Task.Delay(150);
await OnClicked.InvokeAsync();
}
}
// CHA
<ParamChildEvent OnClicked="HandleChildClicked" />
@code {
private void HandleChildClicked(string message)
{
LastClick = message;
}
}
// CON
<button class="btn btn-outline-success" @onclick="HandleClick"> Child Click Me </button>
@code {
[Parameter] public EventCallback<string> OnClicked { get; set; }
private async Task HandleClick()
{
var msg = $"Child clicked at {DateTime.Now:HH:mm:ss}";
await OnClicked.InvokeAsync(msg);
}
}
// CHA (async)
<ParamChildEvent OnClicked="HandleChildClickedAsync" />
@code {
private async Task HandleChildClickedAsync()
{
// CHA xử lý async (API / DB / JS / delay...)
await Task.Delay(300);
LastClick = $"Child clicked at {DateTime.Now:HH:mm:ss}";
}
}
// CON (InvokeAsync chờ CHA xong)
<button class="btn btn-outline-success" @onclick="HandleClick"> Child Click Me </button>
@code {
[Parameter] public EventCallback OnClicked { get; set; }
private async Task HandleClick()
{
// CON xử lý trước
await Task.Delay(150);
// ĐỢI CHA xử lý xong
await OnClicked.InvokeAsync();
// Code ở đây chỉ chạy SAU khi CHA xong
}
}
// CHA
// Gán callback bằng C# (không bó cứng trong markup)
<ParamChildEvent OnClicked="clickCallback" />
@code {
private EventCallback clickCallback;
private bool IsAdmin = true;
protected override void OnInitialized()
{
clickCallback = EventCallback.Factory.Create(
this,
IsAdmin ? HandleAdminClick : HandleUserClick
);
}
private void HandleAdminClick()
{
LastClick = "Admin handled click";
}
private void HandleUserClick()
{
LastClick = "User handled click";
}
}
// CON
<button class="btn btn-outline-success" @onclick="HandleClick"> Child Click Me </button>
@code {
[Parameter] public EventCallback OnClicked { get; set; }
private async Task HandleClick()
{
// Kiểm tra CHA có gán handler hay không
if (OnClicked.HasDelegate)
{
// ĐỢI CHA xử lý xong (nếu CHA là async Task)
await OnClicked.InvokeAsync();
}
// else: callback optional → CON tự xử lý tiếp
}
}
07 · RenderFragment và ChildContent
CHA truyền khối UI vào CON thông qua ChildContent.
Đây là nội dung truyền từ Parent xuống.
Xem code
<ParamChildContent Title="Card Title">
<p>Đây là nội dung truyền từ Parent xuống.</p>
<button class="btn btn-sm btn-outline-primary" @onclick="() => ContentClicks++"> Content button (clicks: @ContentClicks) </button>
</ParamChildContent>
@code {
private int ContentClicks = 0;
}
<div class="border rounded p-2">
<div class="mb-2"><b>@Title</b></div>
<div>
@ChildContent
</div>
</div>
@code {
[Parameter] public string Title { get; set; } = "";
[Parameter] public RenderFragment? ChildContent { get; set; }
}
08 · RenderFragment<T> — template có dữ liệu
CON nhận list + template, CHA quyết định UI hiển thị của từng item bằng ItemTemplate.
SelectedUser: none
Xem code
namespace BlazorSample.Models;
public record User(string Name, int Age);
<ParamChildTemplate Items="@Users">
<ItemTemplate Context="u">
<div class="d-flex justify-content-between border rounded p-2 mb-1">
<span><b>@u.Name</b> (Age: @u.Age)</span>
<button class="btn btn-sm btn-outline-secondary" @onclick="() => SelectedUser = u.Name">Select</button>
</div>
</ItemTemplate>
</ParamChildTemplate>
<p><b>SelectedUser:</b> @SelectedUser</p>
@code {
private List<User> Users = new()
{
new("Alice", 22),
new("Bob", 30),
new("Charlie", 27)
};
private string SelectedUser = "none";
}
@using BlazorSample.Models
@if (Items?.Count > 0)
{
foreach (var item in Items)
{
@ItemTemplate?.Invoke(item)
}
}
else
{
<p class="text-muted">No items</p>
}
@code {
[Parameter] public List<User> Items { get; set; } = new();
[Parameter] public RenderFragment<User>? ItemTemplate { get; set; }
}
09 · Parameter đồng bộ hai chiều
CON hỗ trợ @bind-Value bằng Value + ValueChanged.
TwoWayText: hello
Xem code
<ParamTwoWay @bind-Value="TwoWayText" />
<p><b>TwoWayText:</b> @TwoWayText</p>
@code {
private string? TwoWayText = "hello";
}
<input class="form-control"value="@Value"@oninput="OnInput" />
@code {
[Parameter] public string? Value { get; set; }
[Parameter] public EventCallback<string?> ValueChanged { get; set; }
private async Task OnInput(ChangeEventArgs e)
{
var v = e.Value?.ToString();
Value = v;
await ValueChanged.InvokeAsync(v);
}
}
10 · @ref — gọi phương thức của component con
@ref cho phép component CHA giữ tham chiếu đến component CON
và gọi trực tiếp method public của CON.
Đây là ngoại lệ trong Blazor (không dùng cho data flow).
Xem code (@ref sample)
<!-- Parent (Page) -->
<RefChild @ref="childRef" />
<button @onclick="CallIncrement">Increment (CHA → CON)</button>
<button @onclick="CallReset">Reset</button>
@code {
private RefChild? childRef;
private void CallIncrement()
{
childRef?.Increment();
}
private void CallReset()
{
childRef?.Reset();
}
}
<!-- Child component file name [RefChild.razor] -->
<div>Counter: @Counter</div>
@code {
private int Counter = 0;
public void Increment()
{
Counter++;
}
public void Reset()
{
Counter = 0;
}
}