TagHelper ;如何修改动态添加的子项



asp-itemsRazor "TagHelper" 将为SelectList中的每个值添加一个<option><select>。我想修改这些孩子中的每一个。

具体来说,我想禁用其中一些(即添加disabled="disabled")。

更具体地说,我想动态禁用其中一些;我正在使用角度,所以我可以ng-disabled="{dynamic_boolean_which_determines_disabled}".这意味着该选项一开始可能会被禁用,但在用户进行更改后,该选项可能会被禁用(无需重新加载页面)。Angular 应该照顾到这一点;我认为 Angular 和 TagHelpers 在理论上应该一起工作......

我期望:

我可以以某种方式访问将要创建的子<option>标签的 IE无数(即SelectList中的每个项目一个),迭代子标签和 SetAttribute("禁用")或 SetAttribute("ng-禁用")...

我试过了:

  1. 创建我自己的 TagHelper 以select[asp-items]为目标,并尝试 GetChildContentAsync() 和/或 SetContent 以达到 IE无数<option>标签并迭代它们并处理每个标签,但我认为这只会让我将整个 InnerHtml 修改为字符串; 做一个 String.rereplace 感觉很笨拙,但如果这是我唯一的选择,我可以做到? 即ChildrenContent.Replace("<option", "<option disabled="..."")
  2. 创建我自己的 TagHelper,它针对作为select[asp-items]子元素的option元素,因此我可以单独处理每个元素。这有效,但不适用于asp-items创建的动态添加<option>,它仅适用于我实际放入 cshtml 标记中的"文字"<option>标签。

我认为这会起作用,但并不理想:

  1. 正如我上面所说,我认为我可以将 TagHelper 的动态 asp-items<option></option> <option></option>的结果作为字符串,并进行字符串替换,但我不喜欢直接使用字符串......
  2. 怀疑(我还没有尝试过)我可以自己做asp-items的工作;即custom-items.但是,我正在通过重新做asp-items可以为我做的工作来重新创建轮子?

所以我还没有阅读示例中的"AutoLinkHttpTagHelper",它使用字符串替换(特别是正则表达式替换)来替换URL的每个出现,并指向该URL的<a>。案例略有不同*,但...

无论如何,这是我学会不再担心并喜欢字符串修改后的解决方案:

[HtmlTargetElement("select", Attributes = "asp-items")]
public class AspItemsNgDisabledTagHelper : SelectTagHelper
{
//Need it to process *after* the SelectTagHelper
public override int Order { get; } = int.MaxValue;
//https://learn.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/authoring#ProcessAsync
public AspItemsNgDisabledTagHelper(IHtmlGenerator gen) : base(gen) {}
public override void Process(TagHelperContext context, TagHelperOutput output)
{
//Notice I'm getting the PostContent; 
//SelectTagHelper leaves its literal content (i.e. in your CSHTML, if there is any) alone ; that's Content
//it only **appends** new options specified; that's PostContent
//Makes sense, but I still wasn't expecting it
var generated_options = output.PostContent.GetContent();
//Note you do NOT need to extend SelectTagHelper as I've done here
//I only did it to take advantage of the asp-for property, to get its Name, so I could pass that to the angular function
var select_for = this.For.Name;
//The heart of the processing is a Regex.Replace, just like
//their example https://learn.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/authoring#inspecting-and-retrieving-child-content
var ng_disabled_generated_options = Regex.Replace(
generated_options,
"<option value="(\w+)">",
$"<option value="$1" ng-disabled="is_disabled('{select_for}', '$1')">");
//Finally, you Set your modified Content
output.PostContent.SetHtmlContent(ng_disabled_generated_options);
}

}

学习机会很少:

  1. 以为我会找到AspForTagHelperAspItemsTagHelper,(角度背景建议相应的属性;asp-forasp-items,将是单独的"指令",又名标签助手)。
    1. 事实上,TagHelper的"匹配"侧重于元素名称(不像角度可以匹配元素名称......属性。。。.class。。。CSS选择器)
    2. 因此,我在SelectTagHelper中找到了我想要的东西,它具有ForItems属性。意义。
  2. 正如我上面所说,我扩展了SelectTagHelper,但这没有必要回答我最初的问题。仅当您想像我一样访问this.For.Name时才有必要,但甚至可能有一种方法可以解决这个问题(即在这里重新绑定自己的 For 属性?
    1. 我分心了,认为我需要覆盖SelectTagHelper的行为来实现我的目标;即面向对象的思维。事实上,即使我确实扩展了SelectTagHelper,这也不会阻止基SelectTagHelper的单独实例匹配和处理元素。 换句话说,元素处理发生在管道中。
    2. 这就解释了为什么扩展和调用基数。进程(),将导致选择执行其工作两次;一次是当您的实例匹配时,另一次是当基本实例匹配时。
    3. (我想可以通过创建一个新的元素名称来阻止 SelectTagHelper 匹配,例如<asp-items-select>?但是,没有必要...我只是避免打电话给base.Process().所以除非这是一个不好的做法...

*不同之处在于:

  1. 他们想创建一个不存在的标签,而我想添加一个属性一个已经存在的标签;即<option>
    1. 虽然<option>"标签"是由SelectTagHelper在其PostContent生成的(希望在Content中找到它),而且我不认为按内容模组生成的字符串中的标签可以与他们相应的 TagHelper 匹配——所以也许我们真的是一样的,因为我们只是在处理普通的旧字符串
  2. 他们的"数据"又名"模型"隐含在文本本身中;他们找到一个URL,并且该URL字符串成为他们使用的含义单位。就我而言,有一个用于建模的显式类;SelectList(<select>)由一些SelectListItem(<option>)组成,但这个类对我也没有帮助。
    1. 该类只给我像public bool Disabled这样的属性(请记住,这对我来说是不够的,因为禁用的值可以在浏览器中更改为 true 或 false;即仅限客户端),以及public SelectListGroup Group- 当然没有像ng-disabled那样非标准,也没有像 Attributes 这样的"包罗万象"属性,它可以让我在那里放置任意属性(ng-disabled或其他任何东西)。

最新更新