Study Blazor .NET(四)數據綁定
翻譯自:Study Blazor .NET,轉載請註明。
數據綁定
單向綁定
在blazor中單向綁定簡單而直接,無需UI刷新或渲染。下面示例展示了單向數據綁定:
//Counter.razor
@page "/counter"
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" onclick="@IncrementCount">Click me</button>
@functions {
int currentCount = 0;
void IncrementCount()
{
currentCount++;
}
}
這裡 @currentComponent
的值會根據點擊 Click me
按鈕的次數而增加。<p>
標籤元素的值會自動刷新無需任何其它組件刷新。
雙向綁定
Blazor為雙向綁定提供多種選擇,與一些流行的JS語言相比實現起來更加優雅。
相同組件中使用綁定
在blazor中,bind
特性支援雙向數據綁定,在下面的例子中,checkbox 在同一個組件中使用了 bind
屬性:
//TwoWayBind.razor
@page "/two-way-binding"
<input type="checkbox"
bind="@updateString" />
<p>This string will get value from above text field : @updateString.ToString()</p>
@functions {
Boolean updateString {get; set;} = true;
}
雙向數據綁定也能用lamda表達式通過 onchange
特性實現。不用擔心,Blazor提供了綁定屬性的簡單方法,更多細節如下:
//TwoWayBind.razor
@page "/two-way-binding"
<input type="text"
value="@updateString"
onchange="@((UIChangeEventArgs __e) => updateString = __e.Value.ToString())"/>
<p>This string will get value from above text field: @updateString</p>
@functions {
string updateString = "";
}
bind
特性可以使用值和事件 bind-value-<onwevent>
進行擴展,上面的示例可以用 oninput
代替 onchange
重寫如下:
//TwoWayBind.razor
@page "/two-way-binding"
<input type="text"
bind-value-oninput="@updateString" />
<p>This string will get value from above text field : @updateString</p>
@functions {
string updateString = "";
}
不同組件間使用綁定
方法 1
組件之間傳遞的數據通過組件屬性及其屬性映射完成,這種方法使用 Action<titem>
數據類型。
//ParentComponent.razor
<h3> Parent Component</h3>
<p>String in Parent: @parentString</p>
<button onclick="@PassToChild">Pass String To Child</button>
<ChildComponent
ToChild=@parentString
FromChild=@ReceivedFromChild>
</ChildComponent>
@functions{
private string parentString = "Initial Parent String";
private void PassToChild()
{
parentString += "- To Child";
}
private void ReceivedFromChild(string str)
{
parentString = str;
StateHasChanged();
}
}
//ChildComponent.razor
<h4>Child Component</h4>
<button onclick="@PassToParent">Pass String to Parent</button>
<p>String received from Parent : @ToChild</p>
@functions{
[Parameter]
private string ToChild{get;set;}
[Parameter]
Action<string> FromChild{get;set;}
private string childString;
private void PassToParent()
{
childString = ToChild + "- To Parent";
FromChild(childString);
}
}
ChildComponent中的 FromChild
特性/屬性用 Action<string>
類型從子組件向父組件傳遞值。在父組件中有一個帶有 string
類型參數的響應函數,ChildComponent組件中的按鈕點擊操作觸發這個函數,並且反過來通知 PassToParent
函數,為了通知父組件中的狀態已經改變,我們使用了Blazor內置函數 StateHasChanged()
。
方法 2
這種方法使用 EventCallback
數據類型指定事件變更來傳遞資訊從而代替具體的數據,這裡不需要再調用 StateHasChanged()
函數。
//ParentComponent.razor
<h3> Parent Component</h3>
<p>Logging Event triggered from Child: @logString</p>
<ChildComponent
Trigger=@TriggerFromChild>
</ChildComponent>
@functions{
private string logString = "";
private void TriggerFromChild(UIMouseEventArgs e)
{
logString = e.ToString();
}
}
//ChildComponent.razor
<h4>Child Component</h4>
<button onclick="@Trigger">Trigger Parent</button>
@functions{
[Parameter]
private EventCallback<UIMouseEventArgs> Trigger { get; set; }
}
在ChildComponent中,Trigger
屬性回調ParentComponent中相應的帶有 UIMouseEventArgs
參數的TriggerFromChild
方法,同時在父組件中以字元串形式記錄。
下面是支援的事件參數:
- UIEventArgs
- UIChangeEventArgs
- UIKeyboardEventArgs
- UIMouseEventArgs
方法 3
這裡是組件間雙向綁定的另一種方法,我們可以基於任何事件/方法的執行手動觸發一個 Action
,
//ParentComponent.razor
<h3> Parent Component</h3>
<p>Logging Event triggered from Child: @logString</p>
<ChildComponent
EventFromChild=@TriggerFromChild>
</ChildComponent>
@functions{
private string logString = "";
private void TriggerFromChild()
{
logString = "Triggered From Child using Action and Invoke";
StateHasChanged();
}
}
//ChildComponent.razor
<h4>Child Component</h4>
<button onclick="@Trigger">Trigger Parent</button>
@functions{
[Parameter]
private Action EventFromChild{get;set;}
private void Trigger()
{
EventFromChild?.Invoke();
}
}
在ChildComponent中,用內置函數 Invoke
手動觸發 EventFromChild
Action 屬性,同時回調父組件中相應的 TriggerFromChild
方法,在父組件中,通過 StateHasChanged()
通知狀態已經改變。
級聯值和參數
Blazor提供了一種跨越整個RenderTree(所有組件)傳遞數據的方法,使用 CascadingValue
和 CascadingParameter
代替傳遞組件特性。傳遞的值可以被RenderTree (子組件)通過裝飾屬性 CascadingParameter
代替 Parameter
接收。
//RootComponent.razor
@page "/base-component"
<h3> App Base Component</h3>
<CascadingValue Value="@pName" Name="ProfileName">
<CascadingValue Value="@pAge" Name="ProfileAge">
<ParentComponent/>
</CascadingValue>
</CascadingValue>
@functions {
private string pName {get;set;} = "New To Blazor";
private int pAge {get;set;} = 35;
}
//ParentComponent.razor
<h3> Parent Component</h3>
<p> Profile Name is : @Name and Age is : @Age.ToString(); </p>
@functions{
[CascadingParameter(Name = "ProfileName")]
string Name { get; set; }
[CascadingParameter(Name = "ProfileAge")]
int Age {get;set;}
}
在 CascadingParameter
中,Name
參數必須與具有 CascadingValue
組件的 Name
屬性匹配,如果我們沒有聲明 Name
,那麼 CascadingParameter
中的變數類型與 CascadingValue
中的 Value
屬性匹配。
最好聲明 Name
參數,以避免混淆,這在應用程式規模增長時很有用。
渲染片段,動態內容
除了用特性從父組件向子組件傳遞內容,還可以在組件的 tag 元素中傳遞內容,並且可以使用 RenderFragment
屬性在子組件中呈現。
方式 1
ChildContent
是我們需要在子組件中使用的命名約定,以獲取父組件的內容,
//ParentComponent.razor
<h3> Parent Component</h3>
<ChildComponent>
The line here is passed to child from Parent!!!
</ChildComponent>
//ChildComponent.razor
<h4>Child Component</h4>
<p>@ChildContent</p>
@functions{
[Parameter]
private RenderFragment ChildContent { get; set; }
}
方式 2
RenderFragment
主要用於模板化目的,並基於子組件內部的邏輯呈現內容。
//ParentComponent.razor
<h3> Parent Component</h3>
<ChildComponent>
<SampleText>
<p>
<b>Bold Text here!!!</b>
</p>
</SampleText>
</ChildComponent>
//ChildComponent.razor
<h4>Child Component</h4>
<div>
@for(var i = 1; i <= 3; i++)
{
<h5>Line No : @i</h5>
<div>@SampleText</div>
}
</div>
@functions{
[Parameter]
private RenderFragment SampleText { get; set; }
}
此時 SampleText
不是已經存在的組件,它是在父組件中的 ChildComponent
tag元素中新生成的,同時在子組件中也聲明了相同的命名為 SampleText
的屬性。
SampleText
中的文本在子組件里循環渲染3次,這非常有助於創建模板組件、表格等。