2

【ASP.NET Core】标记帮助器——元素筛选 - 东邪独孤

 1 year ago
source link: https://www.cnblogs.com/tcjiaan/p/17134603.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

【ASP.NET Core】标记帮助器——元素筛选

前一篇中老周从标记帮助的底层介绍关键性的接口,如 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")]

不过,可以根据属性筛选,比如

[HtmlTargetElement("span", Attributes = "[data=1]")]

属性筛选要放在 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# 表达式,编译器能识别。

运行后的结果如下。

367389-20230219170113090-856253241.png

咱们也可以让标记帮助器支持更多元素。

[HtmlTargetElement("span")]
[HtmlTargetElement("div")]
[HtmlTargetElement("p")]
public class ReplaceCharTagHelper : TagHelper

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK