如何在 ASP.NET Core 自定义标签助手中使用视图?

Jam*_*ose 11 asp.net asp.net-mvc asp.net-core asp.net-core-tag-helpers

我一直在关注这篇关于在此处编写自定义标记帮助程序的 Microsoft 文章。

在我看到元素标记在 C# 中硬编码的代码的每个地方

示例(取自上述链接)

public override void Process(TagHelperContext context, TagHelperOutput output)
{
     output.TagName = "section";
     output.Content.SetHtmlContent(
        $@"<ul><li><strong>Version:</strong> {Info.Version}</li>
        <li><strong>Copyright Year:</strong> {Info.CopyrightYear}</li>
        <li><strong>Approved:</strong> {Info.Approved}</li>
        <li><strong>Number of tags to show:</strong> {Info.TagsToShow}</li></ul>");
     output.TagMode = TagMode.StartTagAndEndTag;
}
Run Code Online (Sandbox Code Playgroud)

有没有办法从 cshtml 文件加载标记模板,而不是这样做?(类似于加载部分视图)

我的目的是拥有单独的cshtml文件(每个元素类型一个),以便我可以轻松地设置它们的样式。我的 C# 看起来也很干净!

谢谢,

詹姆士

Joe*_*zar 6

在 Tech Dominator http://blog.techdominator.com/article/using-html-helper-inside-tag-helpers.html的这篇文章中,他们展示了如何以我发现的最简单的方式做到这一点。

这是该文章中出现的示例。我测试了它,效果很好:

using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
using System.Threading.Tasks;

namespace UsingCshtmlTemplatesInTagHelpers.TagHelpers
{
    public class Holder
    {
        public string Name { get; set; }
    }

    public class TemplateRendererTagHelper : TagHelper
    {
        [ViewContext]
        [HtmlAttributeNotBound]
        public ViewContext ViewContext { get; set; }

        private IHtmlHelper _htmlHelper;

        public TemplateRendererTagHelper(IHtmlHelper htmlHelper)
        {
            _htmlHelper = htmlHelper;
        }

        public override async Task ProcessAsync(TagHelperContext context
            , TagHelperOutput output)
        {
            (_htmlHelper as IViewContextAware).Contextualize(ViewContext);

            /*
             * Create some data that are going 
             * to be passed to the view
             */
            _htmlHelper.ViewData["Name"] = "Ali";
            _htmlHelper.ViewBag.AnotherName = "Kamel";
            Holder model = new Holder { Name = "Charles Henry" };

            output.TagName = "div";
            /*
             * model is passed explicitly
             * ViewData and ViewBag are passed implicitly
             */
            output.Content.SetHtmlContent(await _htmlHelper.PartialAsync("Template", model));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Mét*_*ule 5

您可以创建一个局部视图,并从您的TagHelper类中调用它。例如:

<!-- Views/Shared/TagHelpers/MyList.cshtml -->
@model YourInfoClass
<ul>
    <li><strong>Version:</strong> @Model.Version</li>
    <li><strong>Copyright Year:</strong> @Model.CopyrightYear</li>
    <li><strong>Approved:</strong> @Model.Approved</li>
    <li><strong>Number of tags to show:</strong> @Model.TagsToShow</li>
</ul>
Run Code Online (Sandbox Code Playgroud)

在您的TagHelper

[HtmlTargetElement("mylist")]
public class MyListTagHelper : TagHelper
{
    private HtmlHelper _htmlHelper;
    private HtmlEncoder _htmlEncoder;

    public MyListTagHelper(IHtmlHelper htmlHelper, HtmlEncoder htmlEncoder)
    {
        _htmlHelper = htmlHelper as HtmlHelper;
        _htmlEncoder = htmlEncoder;
    }

    public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
    {
        output.TagName = "section";
        output.TagMode = TagMode.StartTagAndEndTag;

        var partial = await _htmlHelper.PartialAsync("TagHelpers/MyList", Info);
        var writer = new StringWriter();
        partial.WriteTo(writer, _htmlEncoder);

        output.Content.SetHtmlContent(writer.ToString());
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 0

我已经实现了类似的东西,但它需要一种迂回的方法,通过依赖注入创建自定义 ViewRendering 服务,并使用它将视图渲染为字符串。我喜欢这种方法,因为它允许我的应用程序将视图用于许多事情,包括电子邮件模板、标签帮助程序以及我需要将视图渲染为字符串以在代码中使用的任何其他情况,同时还允许我将模型传递给更多动态元素的视图。

定义服务的接口:

public interface IViewRenderService
{
    string RenderView(string viewName);

    string RenderView<TModel>(string viewName, TModel model);

}
Run Code Online (Sandbox Code Playgroud)

服务的实现(重要部分):

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Routing;
using System;
using System.IO;

public class ViewRenderService : IViewRenderService
{
    private readonly IRazorViewEngine _viewEngine;
    private readonly ITempDataProvider _tempDataProvider;
    private readonly IServiceProvider _serviceProvider;

    public ViewRenderService(IRazorViewEngine viewEngine, ITempDataProvider tempDataProvider, IServiceProvider serviceProvider)
    {
        _viewEngine = viewEngine;
        _tempDataProvider = tempDataProvider;
        _serviceProvider = serviceProvider;
    }

    public string RenderView(string viewName)
    {
        var actionContext = GetActionContext();

        var viewEngineResult = _viewEngine.FindView(actionContext, viewName, false);

        if (!viewEngineResult.Success)
        {
            throw new InvalidOperationException(string.Format("Couldn't find view '{0}'", viewName));
        }

        var view = viewEngineResult.View;

        using (var output = new StringWriter())
        {
            var viewContext = new ViewContext(
                actionContext,
                view,
                new ViewDataDictionary(
                    metadataProvider: new EmptyModelMetadataProvider(),
                    modelState: new ModelStateDictionary()),
                new TempDataDictionary(
                    actionContext.HttpContext,
                    _tempDataProvider),
                output,
                new HtmlHelperOptions());

            view.RenderAsync(viewContext).GetAwaiter().GetResult();

            return output.ToString();
        }
    }

    public string RenderView<TModel>(string viewName, TModel model)
    {
        var actionContext = GetActionContext();

        var viewEngineResult = _viewEngine.FindView(actionContext, viewName, false);

        if (!viewEngineResult.Success)
        {
            throw new InvalidOperationException(string.Format("Couldn't find view '{0}'", viewName));
        }

        var view = viewEngineResult.View;

        using (var output = new StringWriter())
        {
            var viewContext = new ViewContext(
                actionContext,
                view,
                new ViewDataDictionary<TModel>(
                    metadataProvider: new EmptyModelMetadataProvider(),
                    modelState: new ModelStateDictionary())
                {
                    Model = model
                },
                new TempDataDictionary(
                    actionContext.HttpContext,
                    _tempDataProvider),
                output,
                new HtmlHelperOptions());

            view.RenderAsync(viewContext).GetAwaiter().GetResult();

            return output.ToString();
        }
    }

    private ActionContext GetActionContext()
    {
        var httpContext = new DefaultHttpContext();
        httpContext.RequestServices = _serviceProvider;
        return new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在你的 Startup.cs 中

    public void ConfigureServices(IServiceCollection services)
    {
        ...

        services.AddTransient<IViewRenderService, ViewRenderService>();

        ...
    }
Run Code Online (Sandbox Code Playgroud)

最后,您可以在如下代码中使用它(在本例中是控制器):

public class TestController : Controller
{
    private IViewRenderService viewRenderService;

    public TestController(IViewRenderService _viewRenderService)
    {
        viewRenderService = _viewRenderService;
    }

    public async Task<IActionResult> Index()
    {
        // code

        var stringOfView = viewRenderService.RenderView("EmailTemplate/EmailConfirmation");

        // code that does something with the view as a string

        return View();
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以将视图放在自己文件夹下的 Views 文件夹中(在上面的示例中,视图路径为:/Views/EmailTemplate/EmailConfirmation.cshtml)

如果您的视图需要模型,您可以像这样传递它:

var stringOfView = viewRenderService.RenderView("folder/view", model);
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助!