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

12

我一直在参考微软的这篇关于编写自定义标签助手的文章here

无论在哪里,我都看到代码中元素标记硬编码在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;
}

有没有一种方法可以从cshtml文件中加载标记模板?(类似于加载Partial Views)

我的目的是拥有单独的cshtml文件(每个元素类型一个),这样我就可以轻松地对它们进行样式设置。同时,我的C#代码看起来也会更干净!

谢谢,

詹姆斯


在 MVC 中,我们可以像 helper.Partial 这样调用帮助器对象,但不确定在 ASP.NET Core 中是否也能这样做。 - Ehsan Sajjad
根据您的需求,视图组件可能是一个选择。 - juunas
3个回答

7
您可以创建一个部分视图,并从您的 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>

在您的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());
    }
}

看起来这个答案有问题,因为出现了异常,提示在调用html.PartialAsync之前需要Contextualize(ViewContext)。 - Harry Sarshogh

5
在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));
        }
    }
}

这是最好的答案,但我需要一个StringWriter对象,其中partial可以写入它,然后使用writer输出内容。 - Harry Sarshogh

0
我已经实现了类似的东西,但是采用了一种迂回的方法,通过创建一个具有依赖注入的自定义ViewRendering服务,并使用该服务将视图渲染成字符串。 我喜欢这种方法,因为它允许我的应用程序使用视图来完成许多任务,包括电子邮件模板、标签助手以及任何其他需要将视图渲染为字符串的情况,同时也允许我向视图传递模型以获得更动态的元素。

定义服务的接口:

public interface IViewRenderService
{
    string RenderView(string viewName);

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

}

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

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());
    }
}

然后在你的 Startup.cs 文件中

    public void ConfigureServices(IServiceCollection services)
    {
        ...

        services.AddTransient<IViewRenderService, ViewRenderService>();

        ...
    }

最后,你可以像以下这样在代码中使用它(例如控制器):
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();
    }
}

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

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

var stringOfView = viewRenderService.RenderView("folder/view", model);

希望这可以帮到你!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接