ASP.NET Core:检测标记帮助程序是否与父元素一起呈现

ASP.NET Core: Detect whether a tag helper is rendered with a parent element

提问人:nfplee 提问时间:7/25/2023 最后编辑:nfplee 更新时间:7/27/2023 访问量:88

问:

在 ASP.NET Core 中,是否可以检测标记帮助程序是否使用父元素呈现?

我问的原因是我有一个脚本标签帮助程序,它缓存了它通常会呈现的 HTML,而是我使用中间件在结束 body 标签之前呈现 HTML。但是,如果在部分视图中调用标记帮助程序,那么我想简单地将其呈现到位。

我的第一个想法是添加一个正文标签帮助程序,然后将一个变量添加到请求缓存中,我可以在我的脚本标记帮助程序中检测到该变量。但是,我发现由于正文标签帮助程序位于我的布局页面中,因此它是在我的视图(执行脚本标记帮助程序)之后呈现的。

我想到的另一种解决方案是在操作中存储一个变量,但我不喜欢这样,因为每次返回部分视图时都必须记住这样做。

我将不胜感激任何帮助。

编辑:这里有一些代码供参考。

ScriptTagHelper:

public class ScriptTagHelper : TagHelper {
    private readonly IHtmlManager _htmlManager;

    public ScriptTagHelper(IHtmlManager htmlManager) {
        _htmlManager = htmlManager;
    }

    public string? At { get; set; }

    [HtmlAttributeNotBound, ViewContext]
    public ViewContext ViewContext { get; set; } = default!;

    public override void Process(TagHelperContext context, TagHelperOutput output) {
        if (!string.IsNullOrEmpty(At)) {
            output.SuppressOutput();

            var builder = new TagBuilder("script") {
                TagRenderMode = TagRenderMode.Normal
            };

            foreach (var attribute in output.Attributes) {
                builder.Attributes.Add(attribute.Name, attribute.Value.ToString());
            }

            _htmlManager.RegisterHtml(At, builder);
        }
    }
}

这是我的中间件:

public class RenderHtmlMiddleware {
    private readonly RequestDelegate _next;

    public RenderHtmlMiddleware(RequestDelegate next) {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext httpContext, IHtmlManager htmlManager) {
        var originBody = httpContext.Response.Body;

        using var body = new MemoryStream();
        httpContext.Response.Body = body;

        try {
            await _next(httpContext);
        } finally {
            httpContext.Response.Body = originBody;
        }

        if (body.Length > 0) {
            var content = Encoding.UTF8.GetString(body.ToArray());

            foreach (var position in Enum.GetValues<HtmlPosition>()) {
                var html = await htmlManager.GetHtmlAsync(position);
                var innerHtml = string.Join("", html.Select(h => h.ToHtmlString()));

                ... Code removed for brevity which handles different positions
                
                content = content.Replace("</body>", string.Concat(innerHtml, Environment.NewLine, "</body>"));
            }

            httpContext.Response.ContentLength = Encoding.UTF8.GetByteCount(content);

            await httpContext.Response.WriteAsync(content);
        }
    }
}

现在在我的视图/部分视图中,我可以简单地说:

<script at="@HtmlPosition.BodyPostContent" src="foo"></script>
asp.net-core asp.net-core-tag-helpers

评论

0赞 Ruikai Feng 7/26/2023
“我问的原因是我有一个脚本标签助手,它缓存了它通常会呈现的 HTML,而是我使用中间件在结束 body 标签之前呈现 HTML”我不太理解,任何相关的代码?

答:

0赞 Ruikai Feng 7/26/2023 #1

但是,如果在部分视图中调用标记帮助程序,那么我会 喜欢简单地将其渲染到位。

如果您只想检查它是否在 partialview 中呈现:

您可以添加一个属性:

[HtmlAttributeNotBound]
[ViewContext]
public ViewContext ViewContext { get; set; }

您可以使用 ViewContext 访问 HttpContext/当前视图名称

一个最小的例子:

[HtmlTargetElement("MyTag")]
    public class MyTag : TagHelper
    {

        [HtmlAttributeNotBound]
        [ViewContext]
        public ViewContext ViewContext { get; set; }
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            output.TagName = "label";
            //check if is partial
            var path = ViewContext.View.Path;
            var ispartial = path.Contains("Partial");
           
            if (!ispartial) 
            {
                output.SuppressOutput();
            }
            
            
        }
    }

在“主页”视图中:

<partial name="MyPartial"/>
<MyTag>Home</MyTag>

在部分视图中:

<MyTag>Partial</MyTag>

结果:

enter image description here

仅被渲染<label>partial</label>

更新: 模板的可能解决方案:

public class MyPartial : PartialTagHelper
    {
        public MyPartial(ICompositeViewEngine viewEngine, IViewBufferScope viewBufferScope) : base(viewEngine, viewBufferScope)
        {
        }
        
        
        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) 
        {
            this.ViewContext.HttpContext.Items.Add("IsPartial", true);
            await base.ProcessAsync(context, output);
            this.ViewContext.HttpContext.Items.Remove("IsPartial");
        }
    }


[HtmlTargetElement("MyTag")]
    public class MyTag : TagHelper
    {

        [HtmlAttributeNotBound]
        [ViewContext]
        public ViewContext ViewContext { get; set; }
        

        public ViewLocationExpanderContext ViewLocationExpanderContext { get; set; }
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            output.TagName = "label";

            var ispartial = ViewContext.HttpContext.Items.TryGetValue("IsPartial",out var partial);          

            //access httpcontext            
           
            if (!ispartial) 
            {
                output.SuppressOutput();
            }
            
            
            
        }
    }

在视野中:

@model MyEntity
<my-partial name="MyPartial"model="@Model"/>

@Html.DisplayFor(x=>x.SomeEntity)

部分:

@model MyEntity


@Html.DisplayFor(x=>x.SomeEntity)

模板:

@model SomeEntity

<dl>
    <dd>Id:@Model.Id</dd>
    <dd>Amount:@Model.Amount</dd>
    <dd>Rate:@Model.Rate</dd>    
</dl>
<MyTag>InPartial1</MyTag>
<MyTag>InPartial2</MyTag>

即使 taghelper 是在模板中调用的,也只呈现了部分视图中的那个:enter image description here

评论

0赞 nfplee 7/26/2023
谢谢,我唯一的问题是部分视图可能会调用显示/编辑器模板,该模板本身调用脚本标签帮助程序。因此,如何确定是在局部视图还是标准视图中调用了显示/编辑器模板?
0赞 Ruikai Feng 7/26/2023
嗨,@nfplee您能分享任何相关的代码吗?您是否正在尝试使用 Html.Displayfor/Html.EditorFor ?
0赞 nfplee 7/26/2023
我用一些示例代码更新了我的问题。
0赞 Ruikai Feng 7/27/2023
我已经更新了我的答案,请检查一下
0赞 Ruikai Feng 7/27/2023
创建自定义 partialview 标签助手可以解决您的问题