如何使用ASP.NET Core自定义Tag Helper中的视图?

时间:2017-02-10 20:44:54

标签: asp.net asp.net-mvc asp.net-core asp.net-core-tag-helpers

我一直在关注编写自定义标记助手here的Microsoft文章。

每个我看到C#

中硬编码元素标记的代码

示例(取自上述链接)

#'sigCOMP
#'@description signal comparison operators incl and, or, xor for quantstrat signals.
#'@param label name of the output signal
#'@param data the market data
#'@param columns the signal columns to intersect, if a second level comparison is used, the comparison result must reside in the first column only (compare one 2nd level with a True/False Column) or in both, marked by Keyword '2nd'
#'@param relationship operators gte, gt, lte, lt, eq, and, or, xor  TODO:NOT
#'@param secondComparison vector of columns to intersect, if yes, then also set the relationship comparison
#'@param relationshipSecondComparison operators gte, gt, lte, lt, eq
#'@param offset1 optional
#'@param offset2 optional
#'@return a new signal column that intersects the provided columns
#'@export


sigCOMP <- function (label, data = mktdata, columns, relationship = c("gte", "gt", "lte", "lt", "eq", "and", "or", "xor"),  relationshipSecondComparison = c("gte", "gt", "lte", "lt", "eq"), secondComparison, res_not, offset1 = 0, offset2 = 0) 
{
  ret_sig = NULL
  compcols <- NULL

  if(!missing(columns)){
    if (relationship == "op") {
      if (columns[1] %in% c("Close", "Cl", "close")) 
        stop("Close not supported with relationship=='op'")
      switch(columns[1], Low = , low = , bid = {
        relationship = "lt"
      }, Hi = , High = , high = , ask = {
        relationship = "gt"
      })
    } #whatever that is

    colNums <- NULL  
    for(sec in 1:length(columns)){
      if (columns[sec]=='2nd'){
        colNums <- c(colNums,0)
      }
      else{
        colNums <- c(colNums, match.names(columns[sec], colnames(data)))
      }
    }

    opr <- switch(relationship[1], 
                  gt = , `>` = ">",  
                  gte = , gteq = , ge = , `>=` = ">=",
                  lt = , `<` = "<", 
                  lte = , lteq = , le = , `<=` = "<=",
                  eq = , `==` = , `=` = "==",
                  and = "&",
                  or = "|",
                  xor = "xor"
                  # todo: NOT
    )

  } #perform preparation actions if 1|2 columns exist or else stop 
  else {

      stop("only works if two comparison columns are provided. for true/false evaluations you can add e.g. 2nd 2nd or <Signal>, 2nd ")  


  }


  if (!missing(secondComparison))
    {
      ret_sig2nd <- NULL
      opr2nd <- c(1:length(secondComparison))

        if (length(secondComparison) != length(relationshipSecondComparison)){
          stop("make sure to have a comparison operator for each second level comparison you would like to perform")
        } 
        else {

          for (j in 1:length(relationshipSecondComparison)) {
              # run through pairs of columns and relationship checks and return these in a dataframe ret_sig2nd
              # the return column of the appropriate pair will have the name col1 op col2 e.g. close gt nFast

              colNums2nd <- c(0,0)
              comp2ndPartners <- unlist(secondComparison[j])
              relationship2 <- unlist(relationshipSecondComparison)[j]
              colNums2nd[1] <- match.names(comp2ndPartners[1], colnames(data))
              colNums2nd[2] <- match.names(comp2ndPartners[2], colnames(data))
                opr2nd[j] <- switch(relationship2, 
                                  gt = , `>` = ">",  
                                  gte = , gteq = , ge = , `>=` = ">=",
                                  lt = , `<` = "<", 
                                  lte = , lteq = , le = , `<=` = "<=",
                                  eq = , `==` = , `=` = "==",
                                  and = "&",
                                  or = "|",
                                  xor = "xor"
                                  # todo: NOT
              )
               ret_append <- do.call(opr2nd[j], list(data[, colNums2nd[1]] + offset1, 
                                           data[, colNums2nd[2]] + offset2))  

               colnames(ret_append) <- paste0(comp2ndPartners[1]," ",relationship2[j]," ",comp2ndPartners[2])
               ret_sig2nd <- cbind(ret_sig2nd,ret_append)
               rm(ret_append)
            }

          compcols <- ret_sig2nd  
        } # end of 2nd Comp = 2nd Relationship validity block

      if(ncol(compcols)==1){ # check the case if only one second level comparison exists
        transfer2ndToFirst <- compcols  #assumption is, the second level comparison took place with the first column of the first level
        # if one second level comparison is provided, execute transfer object with second column of first level
        compcols <- transfer2ndToFirst[, 1] #offset already included in second level comparison
        compcols <- cbind(compcols, data[, colNums[2]] + offset2)

      } # provide the transfer object to be used in the first level comparison if only one second level comparison exists
    }
    else { # check the case if no second level comparison exists

      # if no second level comparison is provided, only execute first level
      compcols <- data[, colNums[1]] + offset1
      compcols <- cbind(compcols, data[, colNums[2]] + offset2)
    } # if no second level exists, execute comparison for first level only

    # for all cases, perform the first level comparison with the columns stored in compcols - offset has to be applied before storing to compcols 
    ret_sig <- do.call(opr, list(compcols[, 1] , 
                                 compcols[, 2] ))  

  colnames(ret_sig) <- label
  return(ret_sig)
}

# ### TESTS
# # To compare just two (first level) colums
# rm(testOnlyFirst)
# testOnlyFirst<- sigCOMP(
#   columns=c("nSlow","nFast"),
#   relationship=c("gt"),
#   label='GT'
# )
# 
# 
# #To compare a signal or another T/F value with a second level comparison
# rm(testOneSecond)
# testOneSecond<- sigCOMP(
#   columns=c("2nd","exitLong"),
#   relationship=c("and"),
#   secondComparison =list(c("Close", "nFast")),
#   relationshipSecondComparison = list(c("gt")),
#   label='andGT'
# )
# 
# 
# rm(test2Second)
# test2Second<- sigCOMP(
#   columns=c("2nd", "2nd"),
#   relationship=c("or"),
#   secondComparison =list(c("Close", "nFast"), c("Close", "nSlow")),
#   relationshipSecondComparison = list(c("gt"), c("gt")),
#   label='orGT'
# )
# 
# rm(test2SecondOr)
# test2SecondOr<- sigCOMP(
#   columns=c("2nd", "2nd"),
#   relationship=c("or"),
#   secondComparison =list(c("Close", "nFast"), c("Close", "nSlow")),
#   relationshipSecondComparison = list(c("gt"), c("gt")),
#   label='orGT'
# )
# 
# rm(test2SecondXor)
# test2SecondXor<- sigCOMP(
#   columns=c("2nd", "2nd"),
#   relationship=c("xor"),
#   secondComparison =list(c("Close", "nFast"), c("Close", "nSlow")),
#   relationshipSecondComparison = list(c("gt"), c("gt")),
#   label='orGT'
# )

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

我的目的是拥有单独的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; } 个文件(每个元素类型一个),这样我就可以轻松设置它们的样式。我的C#看起来也很干净!

谢谢,

詹姆斯

3 个答案:

答案 0 :(得分:1)

您可以创建部分视图,并从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());
    }
}

答案 1 :(得分: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文件夹中(在视图路径上方的示例中是:/Views/EmailTemplate/EmailConfirmation.cshtml)

如果你的视图需要一个模型,你会像这样传递它:

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

希望这有帮助!

答案 2 :(得分:0)

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