مقدمه
در درس گذشته، با ساختار کلی یک پروژهی Blazor Web App آشنا شدیم و دیدیم که رابط
کاربری در Blazor از واحدهای سازندهای به نام کامپوننت (Component)
تشکیل شده است. یک کامپوننت، یک قطعهی خودکفا و قابل استفاده مجدد از UI است که
میتواند شامل نشانهگذاری HTML، منطق پردازشی C#، و وضعیت (state) داخلی خود
باشد. این رویکرد کامپوننتمحور، که در فریمورکهای مدرن جاوااسکریپت مانند React و Vue نیز بسیار
رایج است، به ما اجازه میدهد تا رابطهای کاربری پیچیده را به قطعات کوچکتر و قابل مدیریتتر
تقسیم کنیم.
در این درس، به کالبدشکافی یک کامپوننت Razor میپردازیم و یاد میگیریم که چگونه وضعیت
داخلی آن را مدیریت کنیم، به رویدادهای کاربر (مانند کلیک) پاسخ دهیم، دادهها را از کامپوننتهای
والد دریافت کنیم و از متدهای چرخهی حیات برای اجرای منطق در زمانهای مشخص استفاده نماییم.
آناتومی یک کامپوننت Razor
هر کامپوننت در Blazor یک فایل با پسوند .razor است. این
فایل ترکیبی از دو بخش اصلی است:
- بخش نشانهگذاری (Markup): این بخش که معمولاً در بالای فایل قرار دارد، شامل
کدهای HTML استاندارد و سینتکس Razor (که با @ شروع میشود) برای تعریف
ساختار بصری کامپوننت است.
- بلوک @code: این بلوک که در انتهای فایل قرار میگیرد، حاوی تمام کدهای
C# مربوط به آن کامپوننت است. فیلدها، پراپرتیها، متدها و تمام منطق کامپوننت در
اینجا تعریف میشوند.
کامپوننت Counter.razor که در تمپلت پیشفرض وجود دارد، یک مثال عالی برای نمایش این دو بخش است.
اتصال داده و مدیریت رویدادها
تعامل در Blazor بر پایهی یک چرخهی ساده است: یک رویداد کاربر (مانند کلیک دکمه) یک
متد C# را فراخوانی میکند. آن متد وضعیت (state) کامپوننت را تغییر میدهد.
Blazor این تغییر وضعیت را تشخیص داده و به طور خودکار رابط کاربری را بهروزرسانی
(re-render) میکند تا با وضعیت جدید مطابقت داشته باشد.
Counter.razor
@page "/counter"
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
در این مثال، @currentCount یک اتصال دادهی یکطرفه است که مقدار فیلد C# را در
HTML نمایش میدهد. @onclick="IncrementCount" رویداد کلیک دکمه را به متد
IncrementCount متصل میکند. وقتی این متد اجرا شده و currentCount تغییر میکند، فریمورک
Blazor به طور هوشمند تشخیص میدهد که تنها بخش <p> از DOM نیاز به بهروزرسانی دارد و آن را با مقدار جدید جایگزین میکند.
پارامترهای کامپوننت: ارسال داده
برای اینکه کامپوننتها واقعاً قابل استفاده مجدد باشند، ما باید بتوانیم از یک کامپوننت والد،
دادههایی را به کامپوننت فرزند ارسال کنیم. این کار با استفاده از پارامترهای کامپوننت
(Component Parameters) انجام میشود. یک پارامتر، یک پراپرتی عمومی در کلاس کامپوننت
است که با صفت [Parameter] علامتگذاری شده است.
بیایید یک کامپوننت جدید به نام Alert.razor بسازیم که یک پیام و نوع آن (مثلاً "موفقیت" یا
"خطر") را به عنوان پارامتر دریافت کرده و یک کادر هشدار نمایش دهد.
Components/Alert.razor
<div class="alert @CssClass" role="alert">
@Message
</div>
@code {
[Parameter]
public string Message { get; set; }
[Parameter]
public string MessageType { get; set; } = "info";
private string CssClass => $"alert-{MessageType.ToLower()}";
}
اکنون میتوانیم از این کامپوننت در یک صفحهی دیگر (مثلاً Home.razor) استفاده کرده و مقادیر را
به پارامترهای آن پاس دهیم، درست مانند اینکه در حال تنظیم صفتهای یک تگ HTML هستیم.
Pages/Home.razor
@page "/"
<Alert Message="This is a success message!" MessageType="success" />
<Alert Message="This is a warning." MessageType="warning" />
<Alert Message="This is default info." />
متدهای چرخهی حیات کامپوننت
Blazor مجموعهای از متدهای مجازی را فراهم میکند که ما میتوانیم آنها را بازنویسی
(override) کنیم تا کدی را در لحظات کلیدی چرخهی حیات یک کامپوننت اجرا نماییم. دو مورد از
مهمترین این متدها عبارتند از:
- OnInitializedAsync(): این متد یک بار و پس از
اینکه کامپوننت برای اولین بار ایجاد و پارامترهای اولیهی آن تنظیم شدند، فراخوانی میشود. این
بهترین مکان برای انجام کارهای راهاندازی یکباره، مانند خواندن دادههای اولیه از یک
API یا پایگاه داده است.
- OnParametersSetAsync(): این متد هر بار که کامپوننت پارامترهای
خود را از والدش دریافت میکند، فراخوانی میشود (هم در بار اول و هم در بهروزرسانیهای بعدی).
این مکان برای اجرای منطقی که به تغییرات پارامترها وابسته است، مناسب میباشد.
MyComponent.razor
@code {
[Parameter]
public int ItemId { get; set; }
private ItemData _data;
protected override async Task OnInitializedAsync()
{
Console.WriteLine("Component is initializing...");
}
protected override async Task OnParametersSetAsync()
{
_data = await MyDataService.GetItemByIdAsync(ItemId);
}
}