Blazor University (5)组件 — 字面量、表达式和指令
原文鏈接:https://blazor-university.com/components/literals-expressions-and-directives/
字面量、表達式和指令
源代碼[1]
請注意,本節一般不涵蓋 Razor 標記。它不會涵蓋諸如條件輸出、循環等內容。該主題在網絡和書籍中的其他地方得到了廣泛的介紹。
使用組件時,我們可以將信息作為參數傳遞。這些參數可以是簡單類型,例如整數、字符串、布爾值,也可以是復雜類型,例如 Func<T>、Action 或復雜對象的實例。與 HTML 屬性不同,我們不限于可以在純 HTML 文件中表示為值的值。
Blazor 如何解釋我們傳遞給組件的值取決于我們正在設置的 [Parameter] 的類型,以及我們是否通過使用 @ 符號明確了我們的意圖。
參數使用 name=value 格式分配,就像它們在 HTML 標記中一樣。我們是否使用表達式來確定值、指令(一種特殊情況)或文字取決于 @ 符號的位置(或不存在)。
| 不存在 | 字面量 | |
| 右 | 表達式 | |
| 左 | 指令 | <MyComponent @Bind-Value=SomeValue/> |
| 記住這一點的一個簡單方法是“燈泡法”。我們從右到左拼出縮寫 L.E.D[2]。 |
與 HTML 標記一樣,Blazor 標記允許我們選擇是否希望將值括在引號中。以下是等價的。
我個人的偏好是僅在傳遞文字字符串時才將參數值括在引號中。
字面量
任何傳遞給 HTML 屬性的值(不以 @ 開頭)都被視為字面量。將值傳遞給 Blazor 組件上的 [Parameter] 修飾屬性時,情況并非總是如此(我將在此處[3]詳細介紹),但在大多數情況下,此規則適用。
| HTML 屬性 | <input size=8/> | <input size=”8″/> |
| 組件參數 | <MyHeader Text="Hello" Visible=true/> | <h1>Hello</h1> |
MyHeader 的定義如下:
@if?(Visible) {<h1>@Text</h1> }@code {[Parameter]public?bool?Visible?{?get;?set;?}?=?true;[Parameter]public?string?Text?{?get;?set;?} }除了推斷表達式[4](僅適用于組件上的參數,而不適用于 HTML 屬性)之外,在渲染組件時,您在 HTML 中看到的內容實際上就是您在標記中編寫的內容。
表達式
當我們需要渲染包含動態值而不是固定文字值的 HTML 時,我們需要使用表達式。我們通過在我們分配的值之前添加 @ 符號來向 Blazor 表明我們正在使用表達式。然后,Blazor 將嘗試將 @ 后面的文本解釋為一段有效的 C# 代碼,例如成員名稱或方法調用。
給定我們組件中定義以下成員和值的代碼部分:
int?InputSize?=?8; bool?HeaderVisible?=?true; string?HeaderText?=?"Value?of?variable";private?int?DoubleInputSize() {return?InputSize?*?2; }我們希望看到以下內容:
| <input size=@InputSize/> | <input size=”8″/> |
| <input size=@DoubleInputSize()/> | <input size=”16″/> |
| <MyHeader Text=@HeaderText Visible=@HeaderVisible/> | <h1>Value of variable</h1> |
我們甚至可以通過將表達式文本括在括號中來傳遞更復雜的表達式,例如字符串插值和/或計算值。
| <input size=@(InputSize * 3) /> | <input size=”24″/> |
| <input value=@($"Size is {InputSize}") /> | <input value=”Size is 8″/> |
| <input value=@($"Size is {DoubleInputSize()}") /> | <input size=”Size is 16″/> |
計算為復雜類型的表達式
復雜類型也可以作為參數值傳遞給 HTML 屬性和 Blazor 組件的 [Parameter] 屬性。將非簡單值作為表達式傳遞給 HTML 屬性時,Blazor 將使用 ValuePassed.ToString() 呈現該值;當值被傳遞到 Blazor 組件上的 [Parameter] 屬性時,對象本身被傳遞。
以下面的 Person 類為例:
public?class?Person {public?string?Salutation?{?get;?set;?}public?string?GivenName?{?get;?set;?}public?string?FamilyName?{?get;?set;?}public?override?string?ToString()?=>?$"{Salutation}?{GivenName}?{FamilyName}"; }如果我們在其中一個視圖中創建 Person 的實例,我們可以將該實例作為值傳遞給標準 HTML 屬性,并且 Blazor 將使用重寫的 ToString() 方法來表示該值。
<div?class="row"><div?class="col-4">To?HTML?attribute?using?.ToString()</div><div?class="col-8"><input?readonly?value=@MyPerson?/></div> </div>@code {Person?MyPerson;protected?override?void?OnInitialized(){base.OnInitialized();MyPerson?=?new?Person{Salutation?=?"Mr",GivenName?=?"Peter",FamilyName?=?"Morris"};} }第 8 行 聲明 Person 類型的成員
第 13 行 創建 Person 的實例
第 3 行將 Person 實例作為 HTML 屬性傳遞
呈現的 HTML 如下:
<div?class="row"><div?class="col-4">To?HTML?attribute?using?.ToString()</div><div?class="col-8"><input?readonly?value="Mr?Peter?Morris"?/></div> </div>要證明 Blazor 通過引用其他組件來傳遞對象,請創建一個將 Person 實例作為 [Parameter] 的新組件。創建一個名為 PersonView 的新組件并輸入以下標記:
<div?class="row"><div?class="col-2">Salutation</div><div?class="col-10">@Person?.Salutation</div> </div> <div?class="row"><div?class="col-2">Given?name</div><div?class="col-10">@Person?.GivenName</div> </div><div?class="row"><div?class="col-2">Family?name</div><div?class="col-10">@Person?.FamilyName</div> </div> @code {[Parameter]public?Person?Person?{?get;?set;?} }第 14-15 行
聲明一個名為 Person 的屬性,該屬性是 Person 類型,并使用 [Parameter] 屬性對其進行裝飾,以便任何使用組件都可以在 Razor 標記中設置其值。
第 3、7 和 10 行
如果 Person 不為 null,則會顯示 Salutation、GivenName 和 FamilyName。
最后,更改我們的視圖,使其將 MyPerson 傳遞給我們的新 PersonView 組件:
<div?class="row"><div?class="col-4">To?component?as?object</div><div?class="col-8"><PersonView?Person=@MyPerson?/></div> </div>運行應用程序時,我們現在可以看到將 MyPerson 傳遞給 HTML 屬性的結果,以及作為 Person 的實例傳遞給另一個 Blazor 組件的結果。
推斷表達式
將字面量傳遞給使用組件的參數時,Blazor 需要確保傳遞的值與目標兼容。例如,給定具有布爾參數 Visible 的組件 MyHeader,以下組合是有效的。
| <MyComponent Visible=@true/> | 表達式為真。 |
| <MyComponent Visible="@true"/> | 引號中的表達式。 |
| <MyComponent Visible="true"/> | 字符串類型的文字,推斷為表達式@true。 |
| <MyComponent Visible=true/> | 不帶引號的文字字符串,再次推斷為表達式@true。 |
該表的第一行是一個顯式表達式,true。其他行實際上是在嘗試將布爾參數設置為字符串值。在這些情況下,Blazor 將推斷我們的意圖并改為傳遞布爾值。
除非分配的屬性是字符串,否則 Blazor 將取消引用作為參數傳遞給其他組件的值,并假定它們是表達式。下表顯示了標記以及該標記如何轉換為 C#。
| <MyComponent Visible=”true“/> | Visible = true |
| <MyComponent Visible=@HeaderVisible/> | Visible = HeaderVisible |
| <MyComponent Visible=”HeaderVisible“/> | Visible = HeaderVisible |
在分配的屬性是字符串的情況下,Blazor 將假定沒有 @ 分配的值是字面量。
假設我們的使用組件有一個成員 string HeaderText = "Value of variable",并且嵌入組件有一個 [Parameter] 修飾屬性 public string Text { get; set; } 下表顯示了標記以及如何將其轉換為 C#。
| <MyComponent Text=”Hello”/> | Text = “Hello” | “Hello” |
| <MyComponent Text=@HeaderText/> | Text = HeaderText | “Value of variable” |
| <MyComponent Text=”HeaderText”/> | Text = “HeaderText” | “HeaderText” |
| <MyComponent Text=HeaderText/> | Text = “HeaderText” | “HeaderText” |
第一個例子是明確的,在我們的使用組件中沒有名為 Hello 的成員,因此編譯器知道我們打算將 Text 設置為文字字符串。
第二個例子也很明確,因為我們已經通過在我們的值之前添加一個 @ 符號來明確地確定這是一個表達式。
第三個示例不明確,因為我們有一個名為 HeaderText 的成員。Blazor 必須確定這是文字字符串還是表達式。
第四個示例可能看起來像一個明確的成員引用,但由于 HTML 和 Blazor 都支持傳遞不帶引號的值,因此這很容易成為不帶引號的文字字符串。
在 Visual Studio 中很容易看到 Blazor 如何解釋分配。表達式是突出顯示的語法,而文字則不是。
<MyHeader Text=HeaderText Visible=HeaderVisible/>
HeaderText 是一個文本字符串,HeaderVisible 是一個表達式。
<MyHeader Text=@HeaderText Visible=”HeaderVisible“/>
兩者都是表達。
為了避免意外地被推斷的字面量絆倒,我的建議是堅持使用燈泡方法。始終通過在表達式前面加上 @ 符號來明確表達式。
指令
指令是內置宏,可更改從 Razor 標記生成的已轉譯 C# 代碼。通過在標識符前面加上 @ 符號來使用指令,標識符是我們通常期望的 HTML 屬性的名稱或組件屬性的名稱。如果您還沒有這樣做,請閱讀字面量、表達式和指令[5]。
注意: 目前,與 Angular 等其他框架不同,Blazor 不允許開發人員創建自己的指令。
因為分配給指令的值的類型是已知的(它在 C# 代碼中是強類型的),所以值將被推斷為一個表達式。因此,與組件屬性一樣,在賦值的開頭添加 @ 是不必要的,除非我們希望將表達式傳遞給需要字符串值的指令。一個例外是當我們希望傳遞一個 lambda 時;lambda 必須用 @ 符號轉義并用括號括起來。
@onclick=@(?args?=>?Debug.WriteLine("Clicked")?)以下代碼顯示了如何使用 @onclick 指令將 DOM onclick 事件添加到呈現的 H1 元素。
//?Razor?mark-up?with?@ref?directive <h1?@onclick=H1Clicked>Hello,?world!</h1>@code {public?void?H1Clicked(MouseEventArgs?args){System.Diagnostics.Debug.WriteLine("H1?clicked");} }//?Transpiled?C# public?partial?class?Index?:?Microsoft.AspNetCore.Components.ComponentBase {protected?override?void?BuildRenderTree(RenderTreeBuilder?__builder){__builder.OpenElement(0,?"h1");__builder.AddAttribute(1,?"onclick",?EventCallback.Factory.Create<Microsoft.AspNetCore.Components.Web.MouseEventArgs>(this,?H1Clicked));__builder.AddContent(2,?"Hello,?world!");__builder.CloseElement();} }第 2 行
使用 @onclick 指令定義 H1 元素。
第 18 行
顯示如何轉換 @onclick=H1Clicked 指令,以便在呈現的元素上設置 DOM onclick 事件。
適用于 Razor 文件本身的一些標準指令是:
@code
此指令標識應按原樣輸出到生成的 C# 文件中的 C# 代碼塊。單個 Razor 標記文件中可以有多個 @code 指令;Blazor 會將這些整理到轉譯文件中的單個 C# 代碼塊中。
@page
此指令在轉譯的類上生成一個 [PageAttribute],使 Blazor 路由[6](稍后介紹)能夠識別在 URL 中給定特定地址的情況下要呈現哪個組件(頁面)。
@layout
在轉譯的類上生成 [LayoutAttribute]。Blazor 使用它來確定使用哪個布局(如果有)來包裝頁面的內容。
@typeparam
指示 Blazor 從 Razor 標記生成泛型類。
@inject
允許組件指定 Blazor 在創建組件的新實例時需要注入的依賴項。
@attribute
將指定的 DotNet 屬性添加到生成的 C# 類。
以下是一組可應用于當前 Razor 文件正在使用的組件和 HTML 元素的指令示例。例如 <h1 @ref=MyH1Element>Hello</h1>。
@ref
標識當前組件的成員或屬性,該成員或屬性應包含對將呈現的 HTML 元素或組件的引用。在使用 JavaScript 互操作時,這些可以用作引用,或者獲取對 Blazor 嵌入組件的引用,以便我們可以在其上調用方法。
@bind
允許我們將數據綁定(雙向綁定)到正在使用的組件的屬性或 HTML 元素的屬性。
@attributes
將名稱-值[7]對作為 HTML 屬性輸出。
@key
使我們能夠為元素/組件提供唯一標識符,這有助于在渲染[8] HTML 時保持較小的更改增量。
以下是可用的 HTML DOM 元素事件的子集。這些將在組件事件[9]中更詳細地討論。
@onmousemove
@onclick
@onkeypress
@onscroll
指令屬性
源代碼[10]
指令屬性允許我們將附加信息傳遞給指令。如果我們將指令視為一個類,那么指令屬性就是它的成員。
要傳遞這些附加信息,我們需要重復指令,然后附加一個 : 后跟指令支持的屬性名稱。
例如,默認情況下,我們的瀏覽器會將元素的事件向上傳播,直到它最終到達 HTML 文檔本身。一個普通的 HTML + JavaScript 演示將在單擊元素時將文本輸出到控制臺。
<html><body><div?onclick="console.log('Top?level?clicked')"><h1>Top?level</h1><div?onclick="console.log('Second?level?clicked')"><h2>Second?level</h2><div?onclick="console.log('Third?level?clicked')"><h3>Third?level</h3></div></div></div></body> </html>因為 div 元素是嵌套的,所以當其中一個被點擊時,該事件不僅會觸發 div 本身的 onclick 代碼,還會觸發其父 div 上的 onclick,而父元素又會觸發其自己的父級上的事件 – 以此類推,直到當前元素沒有父元素。
單擊第三級標題時的控制臺輸出為了防止事件在元素樹上傳播,JavaScript 在事件上有一個 stopPropagation 方法[11]。Blazor 使用指令屬性防止傳播 - @onclick:stopPropagation。
@page?"/" @using?System.Diagnostics<div?@onclick=TopLevelClicked><h1>Top?level</h1><div?@onclick=SecondLevelClicked?@onclick:stopPropagation><h2>Second?level</h2><div?@onclick=ThirdLevelClicked><h3>Third?level</h3></div></div> </div>@code {private?void?TopLevelClicked(){Debug.WriteLine("Top?level?clicked");}private?void?SecondLevelClicked(){Debug.WriteLine("Second?level?clicked");}private?void?ThirdLevelClicked(){Debug.WriteLine("Third?level?clicked");} }第 4、6 和 8 行都定義了 @onclick 屬性,并聲明了觸發瀏覽器 onclick 事件時要執行的 C# 方法。第 6 行添加了一個額外的 @onclick:stopPropagation 以防止瀏覽器將點擊從第二級傳播到第一級。更多 DOM 事件將在關于 DOM 事件的部分中介紹。
一些指令屬性期望我們以 @directive:attribute="value" 的形式指定一個值。我們將在雙向綁定[12]一節中詳細介紹這一點。
參考資料
[1]
源代碼: https://github.com/mrpmorris/blazor-university/tree/master/src/Components/LiteralsExpressionsAndDirectives
[2]L.E.D: https://en.wikipedia.org/wiki/Light-emitting_diode
[10]源代碼: https://github.com/mrpmorris/blazor-university/tree/master/src/Components/DirectiveAttributes
[11]stopPropagation 方法: https://www.w3schools.com/jsref/event_stoppropagation.asp
總結
以上是生活随笔為你收集整理的Blazor University (5)组件 — 字面量、表达式和指令的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C# 线程问题之死锁
- 下一篇: .NET6之MiniAPI(二十七):M