Как использовать представление внутри вспомогательной функции настраиваемого тега ASP.NET Core?

Я слежу за этой статьей 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;
}

Можно ли вместо этого загрузить шаблон разметки из файла cshtml? (что-то похожее на загрузку частичных просмотров)

Я намерен создать отдельные cshtml файлы (по одному для каждого типа элемента), чтобы я мог легко их стилизовать. Также мой C # выглядел бы чистым!

Спасибо,

Джеймс


person James Poulose    schedule 10.02.2017    source источник
comment
в mvc мы можем вызвать вспомогательный объект, например helper.Partial, но не уверены в ядре asp.net   -  person Ehsan Sajjad    schedule 10.02.2017
comment
В зависимости от ваших потребностей Просмотр компонентов может быть вариантом.   -  person juunas    schedule 11.02.2017


Ответы (3)


Вы можете создать частичное представление и вызвать его из своего 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());
    }
}
person Métoule    schedule 18.08.2017

В этой статье от 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));
        }
    }
}
person Joe Salazar    schedule 31.07.2019

Я реализовал что-то подобное, но здесь используется подход, основанный на создании настраиваемой службы ViewRendering с внедрением зависимостей и использовании ее для рендеринга представления в строку. Мне нравится этот подход, потому что он позволяет моему приложению использовать представления для многих вещей, включая шаблоны электронной почты, taghelpers и любые другие случаи, когда мне нужно преобразовать представление в строку для использования в коде, а также позволяет мне передавать модель в вид для более динамичных элементов.

Интерфейс, определяющий службу:

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/EmailTemplate/EmailConfirmation.cshtml)

Если вашему представлению требуется модель, вы должны передать ее так:

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

Надеюсь это поможет!

person Toby Lortz    schedule 08.06.2017