前一篇中老周從標記幫助的底層介紹關鍵性的介面,如 ITagHelper ,它是一個標誌,用於識別哪些類屬於 Tag Helper。
標記幫助器畢竟是針對 HTML 標記的,所以得篩選。說白了就是我寫的這個幫助器在哪些 HTML 標記上起作用。這就需要拿出一個特性類。
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] public sealed class HtmlTargetElementAttribute : Attribute
我們們看到,這個特性只能應用到類上面。啥類?當然是從 TagHelper 派生的類(或者實現 ITagHelper 介面的類)。
在使用時,我們一般會呼叫帶一個字串引數的建構函式。
public HtmlTargetElementAttribute(string tag)
用字串說明你這個幫助器用到哪個標記上。比如
[HtmlTargetElement("div")] [HtmlTargetElement("a")] [HtmlTargetElement("p")] [HtmlTargetElement("form")]
這個應該好理解,如果設定的是“div”,表明我這個幫助器是在<div>元素上起作用的。
當然,這個特性類也有無引數的建構函式。如果呼叫此建構函式,即未指定 HTML 標記。
[HtmlTargetElement]
這相當於把標記指定為“*”(星號)。
[HtmlTargetElement("*")]
意思就是我這個幫助器是面向所有 HTML 元素的,通吃。
也許各位大夥伴也發現了,這廝篩選元素的方式很像 CSS 的選擇器。對,的確是的。但是,得記住:這貨是面向標記的,而不是特定某個元素的。啥意思?就是說你不能用元素 id 去篩選,比如這樣就不行。
[HtmlTargetElement("#abc")]
不過,可以根據屬性篩選,比如
屬性篩選要放在 Attributes 屬性上,不要和標記名稱寫一起。上面程式碼是篩選有 data = "1" 的span標記。即
<span data="1">...</span>
----------------------------------------------------------------------------------------------------------
好了,概念的東西說得有點多了,我們們來做個例子。
這裡老周寫了一個面向 <span> 的標記幫助器,把此標記的內容中帶有中括號的文字掩蓋掉。比如
<span>我是一隻小小[小鳥]</span>
被中括號裹起來的是“小鳥”,所以把它掩蓋掉,變成“我是一隻小小**”,或“我是一隻小小##”。
標記幫助器程式碼如下:
namespace Test; [HtmlTargetElement("span")] public class ReplaceCharTagHelper : TagHelper { public char MaskChar { get; set; } = '*'; public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { // 下面這行程式碼的作用是讓標記執行它的子級 // 這樣我們才能獲取到目標元素的內容 var tagContent = await output.GetChildContentAsync(); string text = tagContent.GetContent(); int count = text.Length; if(count > 0) { // 原字串的索引不能set,因此先轉為char陣列 var chararr = text.ToArray(); // 這個bool變數是個開關 // 即遇到“[”字元時開,遇到“]”字元時關 // 後面在替換字元時用得上 bool flag = false; for(int x = 0; x < count; x++) { char c = chararr[x]; if(c == '['){ flag = true; //開 continue; } else if(c == ']') { flag = false; //關 continue; } if(flag){ // 如果“開”說明進入了中括號內,表示字元可替換 // 如果“關”說明已經出了中括號,就別替換了 chararr[x] = MaskChar; } } // 構建新的字串 string newStr = new string(chararr); // 把“[”、“]”兩個字元清除 newStr = newStr.Replace("[", "").Replace("]", ""); // 用新的內容替換標記原來的內容 output.Content.SetContent(newStr); } } }
下面老周解釋一下。
1、這個幫助器是面向<span>元素的。
2、MaskChar 屬性允許我們們自己設定掩蓋文字的字元,算是一掩碼吧。
3、在處理HTML輸出時注意這一句:
var tagContent = await output.GetChildContentAsync();
為什麼要呼叫這一句呢?因為我們們要修改<span>與</span>之間的內容,你如果直接訪問 output.Content.GetContent 是什麼也獲取不到的,因為此時<span>的子級內容還沒有呈現。所以啊,為了能獲取到待處理的文字,我們們要先呼叫 GetChildContentAsync 方法。這個方法會先執行子級內容,然後返回內容。
4、這裡老周的處理思路是這樣的。string 型別的例項雖然是 char 的集合,但其索引器是 get 的,不支援 set,即我們們不能直接修改其中某個字元。辦法只能先 ToArray 讓文字變成 char[],然後迴圈裡面每個字元。如果遇到“[”,表明中括號開始了(把 flag 設為 true),從下一個字元起就是中括號包含的內容,需要掩蓋掉;如果遇到“]”字元,說明要離開中括號的包圍圈(flag 設為 false),從下一個字元起就不是中括號中的字元,不能掩蓋。最後,用修改過的 char[] 產生新的字串物件,為了打掃戰場,還要把“[”、“]”去掉。這個直接用 Replace 就行了。
5、呼叫 output.Content.SetContent 方法用新的內容替換原有的內容。
在 Razor 文件中,用 @addTagHelper 指令匯入剛自定義的標記幫助器。
@addTagHelper Test.ReplaceCharTagHelper, TestApp
這裡 TestApp 是標記幫助器所在程式集的名稱,一般與專案名字相同。我這個專案就叫 TestApp。
來,測試一下。
@page @addTagHelper Test.ReplaceCharTagHelper, TestApp <span mask-char="@('#')"> 明天我們去[騎行] </span> <span mask-char="@('*')"> ,順便買幾噸[啤酒]喝 </span>
mask-char 就是類中定義的 MaskChar 屬性,ASP.NET Core 會識別像 mask-char 這樣的寫法,主要是語義明瞭。在設定 MaskChar 屬性時要把值寫在 @( ) 中,不能寫成 mask-char="*",否則編譯不透過的。="*" Razor 引擎預設解析為 string 型別而不是 char,而寫在 @() 中就成了 C# 表示式,編譯器能識別。
執行後的結果如下。
我們們也可以讓標記幫助器支援更多元素。
[HtmlTargetElement("span")] [HtmlTargetElement("div")] [HtmlTargetElement("p")] public class ReplaceCharTagHelper : TagHelper