前言

在第四章元件的介紹中,提到元件可重複使用,但是如果元件的內容都固定了要怎麼重複使用呢?

寫程式的大家都知道,看到重複的邏輯時應當要抽離成方法(Method),當一段重複的邏輯要重構成方法時,要做的步驟如下:

  1. 先分析類似的邏輯中哪些部分完全相同,哪些地方有一點不同
  2. 完全相同的部分,依樣畫葫蘆
  3. 有一點不同的部分,將其中變化的地方外移到參數,由外部傳入

元件也是一樣的,一個可重複使用的元件,可能會有一些地方需要變化,這時候就需要使用到參數來處理,畢竟參數本身就是值的不確定性,今天讓我們一起來認識 Blazor 元件中的參數該如何使用

參數 [Parameter]

我們以一個簡單的元件範例來做說明,這是Bootstrap Card,有10張這樣的卡片要製作,但是不一樣的地方只有內容,此時我們就可以把這份 Card 做成 Card.razor

Bootstrap Card

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div class="card">
    <div class="card-header">
        @CardHeader
    </div>
    <div class="card-body">
        <blockquote class="blockquote mb-0">
            <p>@Text</p>
            <footer class="blockquote-footer">@Author</footer>
        </blockquote>
    </div>
</div>

@code {
    [Parameter]
    public string CardHeader{ get; set; }

    [Parameter]
    public string Text{ get; set; }

    [Parameter]
    public string Author { get; set; }
}

在元件中,替公開屬性上加入 Parameter Attribute,就會被識別為參數 完成這個 Card.razor 之後,在另一個元件中可以呼叫它,看起來會像是這樣

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@page "/ParamterSample"
<h3>ParamterSample</h3>

<Card 
    CardHeader="第一則" 
    Text="寫程式時要保持這種心態:就好像將來要維護你這些程式的人是一位殘暴的精神病患者,而且他知道你住在哪" 
    Author="Martin Golding">
</Card>

<Card 
    CardHeader="第二則" 
    Text="這不是個 bug——這一個未註明的功能特徵" 
    Author="Anonymous">
</Card>

自製卡片元件在瀏覽器上的樣子

在引用其他元件時需注意,元件的參數內容如果需要從C#內指派,要在被指派的成員前面加上@符號

1
<Card CardHeader="@Header" Text="@Text" Author="@Author"></Card>

參數的一點點細節

  • 參數應該要宣告為自動屬性 不應該在參數的存取子內撰寫邏輯,因為是參數的提供給父元件傳值的通道,如果在子元件存取子內有使父元件重新渲染的邏輯,這時候就會產生無窮迴圈
1
2
[Parameter]
public string Text{ get; set; }

如果要操作參數的資料,另外做屬性或方法是比較好的做法

  • 指派內容到元件參數時,不可以執行非同步工作
1
2
3
<Card CardHeader="@await GetHeader" </Card>
// 'await' 運算子只能在非同步方法中使用。 
// 請考慮使用 'async' 修飾詞標記這個方法,並將其傳回類型變更為 'Task'。

如果要從非同步的作業中取值,可以使用元件生命週期的OnInitializedAsync,寫起來會像是這樣

1
2
3
4
5
6
7
8
9
<Card CardHeader="@title" </Card>
@code{
    private string? title;

    protected override async Task OnInitializedAsync()
    {
        title = await GetHeader();
    }
}

生命週期也會專門做文章跟大家探討

  • 不支援使用明確 Razor 運算式來串連文字與運算式結果
1
2
<Card CardHeader="我的卡片 @(Card.Title)" </Card>
// Component attributes do not support complex content (mixed C# and markup).

如果要做字串連接,可以在下方code區域處理字串後在使用

參數傳遞

父元件(Parent component) -> 子元件(Child component)

父元件 Parent.razor

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@page "/Parent"
<h3>父子元件參數傳遞</h3>

<h4>父元件</h4>
<input class="form-control" @bind="ParentMessage">

<Child></Child>

@code {
     [Parameter]
    public string ParentMessage{ get; set; }
}

子元件 Child.razor

1
2
3
4
5
6
7
<h4>子元件</h4>
<input value="@ChildMessage" class="form-control">

@code {
    [Parameter]
    public string ChildMessage{ get; set; }
}

打開父元件的頁面是長這樣

父元件頁面

要在對父元件的Input輸入時,將參數傳給子元件,做法如下

  1. 子元件內加入EventCallback<T>,T 是子元件內部接收外部參數的型別並且命名為Changed

父元件的 ParentMessage 參數,要傳遞到子元件內的 ChildMessage 參數,所以EventCallback<T> 會是ChildMessage的型別也就是stringEventCallback的參數名稱 必須 是子元件參數名稱加上Changed,寫起來會像是這樣 子元件內處理EventCallback

1
2
[Parameter]
public EventCallback<string> ChildMessageChanged { get; set; }
  1. 父元件中 bind 子元件內的參數即可
1
<Child @bind-ChileMessage="ParentMessage"></Child>

如此就實現父傳子的功能

如果命名不想要受到 慣例(Convention) 的影響,只要將父元件的 bind 拆開處理即可

子元件(Child component) -> 父元件(Parent component)

在子元件內加入事件,對 Input 輸入完成後,透過事件去更新子元件的欄位,去呼叫 ChildMessageChanged,這樣的做法被稱為 鏈式綁定(Chained bind)

子元素內的異動

1
2
3
4
5
private async Task UpdateMessageFromChild(ChangeEventArgs args)
{
    ChildMessage = args.Value.ToString();
    await ChildMessageChanged.InvokeAsync(ChildMessage);
}

小結

我們了解了元件該怎麼設定參數,還有非常重要的參數傳遞的方法,下一章介紹元件的一生是怎麼樣過的會,來了解它的生命中會遇到什麼事件