Blazor WebAssembly 的核心就是元件,所以今天也是介紹元件的觀念。


當我們建立元件後,外部除了使用 UI 外,其實是可以存取公開成員的,只需要透過 @ref 指示詞,將 @ref 屬性新增至子元件,並且使用與子元件相同的類型來定義欄位,就可以輕鬆取得元件的公開成員了,如:

1
2
3
4
5
<Child @ref="Child"></Child>

@code {
private Child Child;
}

要注意元件渲染完成後才會填入子元件的實體!也就是 OnAfterRender/OnAfterRenderAsync 方法後才能夠存取其成員。

在使用子元件的方法時,如果直接這樣寫:

1
<button type="button" @onclick="Child.PublicFunction">Child Funtion</button>

執行時期就會直接噴錯,因為 Child 實體還沒有被填充,這時候就可以透過表達式來確保在指派事件處理常式之前,會先指派參考變數:

1
<button type="button" @onclick="@(()=> Child.PublicFunction())">Child Funtion</button>

倘若是透過迴圈來產生子元件,要取得所有實體可以參考此技巧:

1
2
3
4
5
6
7
8
9
@for (int i = 0; i < 10; i++)
{
<Child @ref="component" />
}

@code {
Child component { set => components.Add(value); }
List<Child> components = new List<Child>();
}

這樣在被填入實體時,設定的方法會轉為新增在列表中,但如果你是懶人,不想要使用這方式,這邊推薦你一個黑魔法:

1
2
3
4
5
6
7
8
@for (int i = 0; i < 10; i++)
{
<Day18SampleChild @ref="components.Add((Day18SampleChild)__value);//" />
}

@code {
List<Child> components = new List<Child>();
}

這邊的 __value 與後面的 // 我還沒搞清楚,如果之後弄懂了會再發一篇來講解,或者有路過大神再請大神幫忙講解,感謝。

以上就是存取元件實體的方法,是不是很簡單啊?


但存取元件實體這種事情,有時候是為了實現 Focus, Click 等等事件時的目標,但這個好像並不能解決耶?

那是因為 Blazor WebAssembly 在渲染時,並不會像 Angular(或者其他前端框架) 多包一層 component 的標籤,也就是 Blazor WebAssembly 的 Component 並不會存在於 UI 上

那麼如果我們需要在特定事件發生時,Focus 在指定元素時,我們應該怎麼處理?

這時候一樣是透過 @ref 來取得元素實體參考,但對象不能是元件,而是 input, button 等等原生標籤,並且以 ElementReference 型別來做為參數類型:

1
2
3
4
5
<input @ref="inputRef" />

@code{
private ElementReference inputRef;
}

但是 ElementReference 型別只有一個 Id 成員,並不能實現上面的需求,因為這時候是需要將資訊交給 JavaScript,由 JavaScript 來幫我們處理。

雖然開場說大話表示不用寫 JavaScript 也能寫網頁,但那種情況真的滿狹隘的,除非之後的更新處理好了這塊。

因為本篇的重點不想放太多在 JavaScript 的使用上,會在後續文章將針對JS interop詳細介紹,所以這邊將快速帶過用法。

建立 exampleJsInterop.js 檔案其內容為:

1
2
3
4
5
window.exampleJsFunctions = {
focusElement: function (element) {
element.focus();
}
}

記得在 index.html 中引用:

1
<script src="../js/exampleJsInterop.js"></script>

然後回到我們的元件來,只需要注入 IJSRuntime 並使用其 InvokeVoidAsync 方法就可以使用剛剛撰寫的 JS 方法了。

1
2
3
4
5
6
7
8
9
@inject IJSRuntime JSRuntime

@code {
public async Task SetFocus()
{
await JSRuntime.InvokeVoidAsync(
"exampleJsFunctions.focusElement", InputRef);
}
}

只要呼叫 SetFocus 方法,就能夠正確 focus 在 input 上了,這樣了解了嗎?


以上就是元件參考的取得方法了,是不是很簡單呀?一樣有我寫的範例程式碼 - Day18

感謝大家的閱讀,我們明天見。

參考資料
捕獲元件的參考
Capture references to multiple similar child-components #13358