一.前言
在第一篇文章中,有提到過組件(Component)這個概念。組件在 Blazor 中是必不可少的,UI 全靠它組裝起來,和前端的 JS 組件是一個意思,比如:vue component、react component 等等。借用官方文檔的描述:
Blazor 應用是使用組件構建的。 組件是自包含的用戶界面 (UI) 塊,例如頁、對話框或窗體。 組件包含插入數據或響應 UI 事件所需的 HTML Tag和處理邏輯。 組件非常靈活且輕量。 可在項目之間嵌套、重復使用和共享。
二.組件
組件一般以 .razor 為文件名后綴,且組件名必須以大寫字母開頭(猜測可能是和VUE里的命名限制一樣,表面和Html標簽名重復)。
我們新建的項目,Shared 文件夾中就有三個組件:
左側導航菜單組件:
在主布局組件中應用了導航菜單組件:
更多關於組件的資料請查閱官方文檔:創建和使用 ASP.NET Core Razor 組件
三.數據綁定
1.介紹
Razor 組件通過名為 @bind
的HTML元素屬性提供數據綁定功能,這個綁定是雙向的。
@bind
是區分大小寫的,例如:@BIND
、@Bind
都是錯誤的,下面寫了一個例子,將 CurrentValue
綁定到兩個文本框中。
<div class="row">
<div class="col-6">
<input class="form-control" type="text" @bind="CurrentValue" />
</div>
<div class="col-6">
<input class="form-control" type="text" @bind="CurrentValue" />
</div>
</div>
<div class="row">
<button class="btn btn-primary" @onclick="ChangeValue">變 更</button>
</div>
@code
{
public int CurrentValue { get; set; } = 0;
private void ChangeValue()
{
CurrentValue ++;
}
}
需要注意的是在文本框的綁定中,僅當呈現組件時,UI才會更新文本框,而不響應於更改屬性的值。由於組件是在事件處理程序代碼執行后呈現的,因此屬性更新通常在觸發事件處理程序后立即反映在UI中。
@bind="CurrentValue"
等同於以下代碼:
<input value="@CurrentValue"
@onchange="@((ChangeEventArgs __e) => CurrentValue =
__e.Value.ToString())" />
@code {
public int CurrentValue { get; set; } = 0;
}
點擊按鈕,變更了值,也會應用到文本框中:
2.變更綁定事件
上面小節中,默認綁定了 onchange
事件,只有文本框失去焦點才會觸發,體驗不是很好,那么可不可以在輸入的時候就同步更新值呢,當然是可以的,解決方案就是變更綁定事件為 oninput
,通過設置@bind:event
屬性來變更綁定事件:
<div class="col-6">
<input class="form-control" type="text" @bind="CurrentValue" @bind:event="oninput" />
</div>
<div class="col-6">
<input class="form-control" type="text" @bind="CurrentValue" @bind:event="oninput" />
</div>
3.輸入錯誤的值
我們設置的 CurrentValue 的類型是 int ,如果我們輸入字母,那么字母將不會被接受,同時值會恢復到輸入前的正確值。
4.子父組件數據傳遞
在 vue、react 等 js 中,都有子父組件傳值概念,Blazor 也不例外。
(1)父傳子
新建一個子組件命名為 ChildComponent
<div class="row">
<h2>子組件</h2>
</div>
<div class="row">
<span>Year: </span> <input class="form-control" type="text" value="@Year" />
</div>
@code {
[Parameter]
public int Year { get; set; }
[Parameter]
public EventCallback<int> YearChanged { get; set; }
}
定義一個 Year
屬性和 EventCallback<int>
類型的屬性 YearChanged
新建一個父組件命名為ParentComponent
<div class="row">
<h2>父組件</h2>
</div>
<div class="row">
<span>ParentYear: </span> <input class="form-control" type="text" @bind="ParentYear" @bind:event="oninput"/>
</div>
<ChildComponent @bind-Year="ParentYear" />
@code {
[Parameter]
public int ParentYear { get; set; } = 1978;
}
在頁面中引用父組件:
YearChanged
是一個約定命名,不能更改,更改將會報錯:
EventCallback 用於子父組件嵌套時公開事件,比如 YearChanged 就公開了子組件 Year 屬性的 changed 事件。父組件里,通過 @bind-Year
來綁定 Year
的 changed 事件,然后將父組件 ParentYear
的值傳遞過去,達成父級組件向子級組件傳遞值。
<ChildComponent @bind-Year="ParentYear" />
等同於 <ChildComponent @bind-Year="ParentYear" @bind-Year:event="YearChanged" />
,如果使用后者,那么事件命名將不會受約定命名限制。
運行效果:
(2)子傳父(鏈式綁定)
子傳父,無法直接通過 @bind
來實現,需要單獨指定事件處理程序和值,我們更改上面的子組件,定義一個 OnYearChanged
事件,並將其綁定到文本框的 oninput
事件,在事件里手動更新了 Year的值,並調用 YearChanged 來進行傳遞。
<div class="row">
<h2>子組件</h2>
</div>
<div class="row">
<span>Year: </span> <input class="form-control" type="text" @oninput="OnYearChanged" value="@Year" />
</div>
@code {
[Parameter]
public int Year { get; set; }
[Parameter]
public EventCallback<int> YearChanged { get; set; }
private Task OnYearChanged(ChangeEventArgs e)
{
Year = int.Parse(e.Value.ToString());
return YearChanged.InvokeAsync(Year);
}
}
運行: