ASP.NET Core Blazor Webassembly 之 資料綁定
上一次我們學習了Blazor元件相關的知識(Asp.net Core Blazor Webassembly - 元件)。這次繼續學習Blazor的資料綁定相關的知識。當代前端架構都離不開資料綁定技術。資料綁定技術以資料為主導來驅動UI界面,使用者對資料的修改會實時提現在UI上,極大的提高了開發效率,讓開發者從繁瑣的dom操作中解脫出來。對于資料綁定.NET開發者并不會陌生,WPF裡大量應用資料綁定技術,有過WPF開發經驗的同學其實很容易了解前端的資料綁定。總之資料綁定技術及其概念、思維極其重要。下面讓我們看看Blazor的資料綁定技術。
單向綁定
Blazor的資料綁定官方文檔是直接從雙向綁定開始的,但我覺得有必要說一下單向綁定。因為其他架構一般都會區分單向、雙向,比如vue的v-bind單向,v-model就是雙向。我們這裡分開講也有利于跟其他架構進行對比。下面我們實作一個計數器元件來示範下單向資料綁定。
使用@進行綁定
@page "/counter"
Current count: @currentCount
Click me
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
這個Counter元件預設的項目就自帶。跟我們使用服務端Razor一樣,使用@符号在需要替換值的地方插入對應的變量。這個值就會被渲染在相應的地方。當我們在前端修改變量的時候,對應的ui界面會同步進行修改。
使用@bind-{attribute}進行綁定
除了直接使用@進行綁定,我們還可以使用@bind-{attribute}來實作對html元素屬性的綁定,比如對style,class内容進行綁定。下面示範下對class進行綁定。我們把p元素的class綁定到“currentClass”字段。
Counter
current count: @currentCount
private string currentClass = "text-danger";
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
使用@bind-{attribute}進行綁定有個比較奇怪的問題,當你使用@bind-{attribute}進行綁定的時候必須同時指定@bind-{attribute}:event。@bind-{attribute}:event是用來指定雙向綁定的時候控件在發生某個事件的時候回寫值到綁定的字段上。可是p,div這種元素根本不可能會激發onchange,oninput這種事件,也不可能去修改綁定的字段的值,這個用法感覺有點多此一舉。
Blazor的單向資料綁定的用法跟ASP.NET Core MVC的Razor基本相似,不同點就是Blazor不需要Http回發到伺服器就可以實時渲染新的界面出來。
雙向綁定
雙向綁定主要使用在一些輸入控件上,比如input,select等。當我們對這些控件上的值進行修改後會回寫綁定的字段。這種特性在表單場景中非常有用。我們定義一個使用者資訊編輯的元件來示範下:
@page "/infoedit"
userName: @userName
sex: @sex
userName: <input @bind="userName" />
sex:
<select @bind="sex">
<option value="m">男</option>
<option value="f">女</option>
</select>
private string userName="abc";
private string sex="f";
當我們運作這個元件,在文本框進行修改後,滑鼠點選其他地方讓文本框失去焦點值就會回寫到綁定的字段上,上面的單向綁定資訊會自動同步。但是如果你用過VUE或者Angularjs的雙向綁定就會覺得失去焦點再回寫字段資料太慢了,一點也不酷。要知道VUE的雙向綁定可是實時同步的,那麼Blazor如何做到在輸入的同時就更新值呢,答案是使用@bind:event來指定回寫的激發事件,我們改成“oninput”事件就可以實作:
userName: <input @bind="userName" @bind:event="oninput"/>
雙向綁定的多種寫法
看到這裡也許你也明白了,@bind真正的本質是由對value的綁定和對某個事件的綁定協同完成的。這點跟VUE非常相似。@bind其實是@bind-value的縮寫,我們可以用@bind-value來實作雙向綁定:
userName: <input @bind-value="userName" @bind-value:event="oninput"/>
以上寫法的效果跟@bind一模一樣。再進一步,@bind-value也隻是對@的包裝,我們可以使用@來實作雙向綁定:
userName: @userName
sex: @sex
userName: <input value="@userName" @oninput="oninput"/>
sex:
<select @bind="sex">
<option value="m">男</option>
<option value="f">女</option>
</select>
private string userName="abc";
private string sex="f";
private void oninput(ChangeEventArgs e)
{
userName = e.Value.ToString();
}
以上代碼的效果跟@bind一模一樣。通過使用@對value直接進行綁定以及綁定一個oninput事件進行值的回寫,同樣實作了雙向綁定。
格式化時間字元串
使用@bind:format 可以對綁定時間類型字段的時候進行格式化:
出生日期:
這個功能有點類似Angularjs的filter功能,但是目前隻能對時間進行格式化,功能很弱。
父元件綁定資料到子元件
元件之間往往都是嵌套的,很多子元件都依賴父元件的資料來決定如何呈現,這種場景非常常見。我們還是繼續修改上面的編輯元件,使用者資訊不在自己初始化,而是從父元件傳遞過來:
子元件:
====================child==================
userName: <input @bind="UserInfo.UserName" />
sex:
<select @bind="UserInfo.Sex">
<option value="m">男</option>
<option value="f">女</option>
</select>
BrithDay:<input @bind="UserInfo.BrithDay" />
[Parameter]
public UserInfo UserInfo { get; set; }
[Parameter]
public EventCallback<UserInfo> UserInfoChanged { get; set; }
子元件定義一個UserInfo對象并且使用[Parameter]進行标記,同時如果父元件使用@bind-UserInfo來綁定的話,還必須實作一個UserInfoChanged事件。
父元件:
@page "/"
====================parent==================
userName: @userInfo.UserName
sex: @userInfo.Sex
brithday: @userInfo.BrithDay
private UserInfo userInfo;
protected override void OnInitialized()
{
userInfo = new UserInfo
{
UserName = "abc",
Sex = "f",
BrithDay = DateTime.Now
};
base.OnInitialized();
}
父元件初始化一個UserInfo對象後通過@bind-UserInfo綁定給子元件。注意這裡我們修改子元件的值并不會同步給父元件,是以可以看到@bind-UserInfo的傳值還是單向的。
子元件傳值給父元件 ??
原來我以為父元件使用@bind-UserInfo并且子元件實作了對應的changed方法就可以實作子元件跟父元件的自動傳值,就跟input的雙向綁定一樣。但是不管我怎麼試都沒有卵用。如果隻是單向的那為什麼要這麼大費周章?我直接使用屬性指派不就可以了麼?像下面這樣:
直接通過元件的屬性直接把父元件的資料傳遞到子元件,效果跟上面是一樣的,而且這樣子元件我還能少寫一個changed事件。我原本以為使用基本類型,比如string可以自動雙向綁定,然後并沒有什麼卵用。沒有辦法我繼續嘗試父元件監聽UserInfoChanged事件來接受子元件的資料,然後VS提示我同一個事件不能綁定兩次。
我已經無語了,難道要我再定義一個事件嗎?于是我放棄了@bind-來實作子元件給父元件傳值,我直接使用屬性指派難道不比這個簡單嗎?
子元件修改資料的時候不斷對外抛事件:
userName: <input @bind="UserInfo.UserName" @oninput="InvokeChanged"/>
sex:
<select @bind="UserInfo.Sex">
<option value="m">男</option>
<option value="f">女</option>
</select>
BrithDay:<input @bind="UserInfo.BrithDay" />
[Parameter]
public UserInfo UserInfo { get; set; }
[Parameter]
public EventCallback<UserInfo> UserInfoChanged { get; set; }
private void InvokeChanged()
{
UserInfoChanged.InvokeAsync(this.UserInfo);
Console.WriteLine("InvokeChanged");
}
父元件監聽事件後更新資料:
====================parent
`
==================
userName: @userInfo.UserName
sex: @userInfo.Sex
brithday: @userInfo.BrithDay
title: @title
private UserInfo userInfo;
private string title;
protected override void OnInitialized()
{
userInfo = new UserInfo
{
UserName = "abc",
Sex = "f",
BrithDay = DateTime.Now
};
base.OnInitialized();
}
private void HandleUserInfoChanged(UserInfo info)
{
this.userInfo.UserName = info.UserName;
Console.WriteLine("HandleUserInfoChanged");
}
我原以為這樣就沒什麼問題了,可奇怪的是,父元件頁面重新渲染需要在子元件第二次修改資料後呈現且呈現的是前一次的。
到這裡我已經無語了,最後我隻能在子元件直接添加一個按鈕,修改完後點選儲存來觸發InvokeChanged事件,這樣子是可以的:
userName: <input @bind="UserInfo.UserName" />
sex:
<select @bind="UserInfo.Sex">
<option value="m">男</option>
<option value="f">女</option>
</select>
BrithDay:<input @bind="UserInfo.BrithDay" />
儲存
[Parameter]
public UserInfo UserInfo { get; set; }
[Parameter]
public EventCallback<UserInfo> UserInfoChanged { get; set; }
private void InvokeChanged()
{
UserInfoChanged.InvokeAsync(this.UserInfo);
Console.WriteLine("InvokeChanged");
}
到此資料綁定也示範完了,可是關于子元件往父元件傳值的事我實在沒像明白,難道是我哪裡錯了?
最後附上代碼:BlazorWasmDataBind
作者:Agile.Zhou(kklldog)
原文位址
https://www.cnblogs.com/kklldog/p/blazor-wasm-databind.html