مقدمه

در درس گذشته، با ساختار کلی یک پروژه‌ی 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) می‌کند تا با وضعیت جدید مطابقت داشته باشد.

Copy Icon Counter.razor
@page "/counter"

<PageTitle>Counter</PageTitle>
<h1>Counter</h1>

<!-- One-way data binding: Displaying the value of the C# variable -->
<p>Current count: @currentCount</p>

<!-- Event handling: Binding the button's click event to a C# method -->
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    // The component's state
    private int currentCount = 0;

    // The method that modifies the state
    private void IncrementCount()
    {
        currentCount++;
    }
}

در این مثال، @currentCount یک اتصال داده‌ی یک‌طرفه است که مقدار فیلد C# را در HTML نمایش می‌دهد. @onclick="IncrementCount" رویداد کلیک دکمه را به متد IncrementCount متصل می‌کند. وقتی این متد اجرا شده و currentCount تغییر می‌کند، فریم‌ورک Blazor به طور هوشمند تشخیص می‌دهد که تنها بخش <p> از DOM نیاز به به‌روزرسانی دارد و آن را با مقدار جدید جایگزین می‌کند.

پارامترهای کامپوننت: ارسال داده

برای اینکه کامپوننت‌ها واقعاً قابل استفاده مجدد باشند، ما باید بتوانیم از یک کامپوننت والد، داده‌هایی را به کامپوننت فرزند ارسال کنیم. این کار با استفاده از پارامترهای کامپوننت (Component Parameters) انجام می‌شود. یک پارامتر، یک پراپرتی عمومی در کلاس کامپوننت است که با صفت [Parameter] علامت‌گذاری شده است.

بیایید یک کامپوننت جدید به نام Alert.razor بسازیم که یک پیام و نوع آن (مثلاً "موفقیت" یا "خطر") را به عنوان پارامتر دریافت کرده و یک کادر هشدار نمایش دهد.

Copy Icon Components/Alert.razor
<!-- The UI changes based on the 'MessageType' parameter -->
<div class="alert @CssClass" role="alert">
    @Message
</div>

@code {
    // A parameter to receive the message text from the parent.
    [Parameter]
    public string Message { get; set; }

    // Another parameter to determine the alert's style.
    [Parameter]
    public string MessageType { get; set; } = "info"; // Default value
    
    private string CssClass => $"alert-{MessageType.ToLower()}";
}

اکنون می‌توانیم از این کامپوننت در یک صفحه‌ی دیگر (مثلاً Home.razor) استفاده کرده و مقادیر را به پارامترهای آن پاس دهیم، درست مانند اینکه در حال تنظیم صفت‌های یک تگ HTML هستیم.

Copy Icon 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." /> <-- Uses the default MessageType -->

متدهای چرخه‌ی حیات کامپوننت

Blazor مجموعه‌ای از متدهای مجازی را فراهم می‌کند که ما می‌توانیم آن‌ها را بازنویسی (override) کنیم تا کدی را در لحظات کلیدی چرخه‌ی حیات یک کامپوننت اجرا نماییم. دو مورد از مهم‌ترین این متدها عبارتند از:

  • OnInitializedAsync(): این متد یک بار و پس از اینکه کامپوننت برای اولین بار ایجاد و پارامترهای اولیه‌ی آن تنظیم شدند، فراخوانی می‌شود. این بهترین مکان برای انجام کارهای راه‌اندازی یک‌باره، مانند خواندن داده‌های اولیه از یک API یا پایگاه داده است.
  • OnParametersSetAsync(): این متد هر بار که کامپوننت پارامترهای خود را از والدش دریافت می‌کند، فراخوانی می‌شود (هم در بار اول و هم در به‌روزرسانی‌های بعدی). این مکان برای اجرای منطقی که به تغییرات پارامترها وابسته است، مناسب می‌باشد.
Copy Icon MyComponent.razor
@code {
    [Parameter]
    public int ItemId { get; set; }
    
    private ItemData _data;
    
    // Called once when the component is created.
    protected override async Task OnInitializedAsync()
    {
        Console.WriteLine("Component is initializing...");
    }

    // Called every time parameters (like ItemId) change.
    protected override async Task OnParametersSetAsync()
    {
        // This is a good place to load data based on a parameter.
        _data = await MyDataService.GetItemByIdAsync(ItemId);
    }
}