Blazor WebAssembly 修仙之途 - 元件與資料繫結

曉晨Master發表於2020-06-01

一.前言

在第一篇文章中,有提到過元件(Component)這個概念。元件在 Blazor 中是必不可少的,UI 全靠它組裝起來,和前端的 JS 元件是一個意思,比如:vue component、react component 等等。借用官方文件的描述:

Blazor 應用是使用元件構建的。 元件是自包含的使用者介面 (UI) 塊,例如頁、對話方塊或窗體。 元件包含插入資料或響應 UI 事件所需的 HTML Tag和處理邏輯。 元件非常靈活且輕量。 可在專案之間巢狀、重複使用和共享。

二.元件

元件一般以 .razor 為檔名字尾,且元件名必須以大寫字母開頭(猜測可能是和VUE裡的命名限制一樣,表面和Html標籤名重複)。

我們新建的專案,Shared 資料夾中就有三個元件:

image-20200529233527594

左側導航選單元件:

image-20200529233556081

在主佈局元件中應用了導航選單元件:

image-20200529233635146

更多關於元件的資料請查閱官方文件:建立和使用 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;
}

在頁面中引用父元件:

image-20200530002006405

YearChanged 是一個約定命名,不能更改,更改將會報錯:

image-20200530002227725

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);
    }
}

執行:

四.資料

相關文章