Как использовать представление внутри ASP.NET Core с пользовательским Tag Helper?
Я следил за этой статьей 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# выглядел бы чистым!
Спасибо,
Джеймс
2 ответа
Вы можете создать частичное представление и вызвать его из вашего 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());
}
}
В этой статье 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));
}
}
}
Я реализовал что-то вроде этого, но он требует подхода, основанного на создании пользовательского сервиса 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/EmailTemplate/EmailConfirmation.cshtml)
Если вашему представлению требуется модель, вы должны передать ее следующим образом:
var stringOfView = viewRenderService.RenderView("folder/view", model);
Надеюсь это поможет!