【问题标题】:Can't define a custom renderer lambda in MVC无法在 MVC 中定义自定义渲染器 lambda
【发布时间】:2013-03-12 13:17:30
【问题描述】:

我想在我的 MVC 视图中将自定义渲染器定义为 lambda,我可以在部分中使用它来多次渲染相同的东西。我计划将其存储在视图数据中。到目前为止,我已经创建了这个扩展方法来存储渲染器:

public static class HtmlHelperExtensions
{
   public static void DefineRenderer<TModel>(this HtmlHelper<TModel> html, string rendererName, Action renderer)
   {
      html.ViewData["_Renderer" + rendererName] = renderer;
   }
}

我正在尝试在我的视图中定义渲染器,但它不起作用;我假设我的语法是关闭的。有人可以告诉我我在这里做错了什么吗?我只希望它在调用时呈现测试段落:

@Html.DefineRenderer("AnalysisTableHeader", () => {
    <p>test paragraph</p>
@});

【问题讨论】:

  • 究竟想达到什么目的? @helper 对你来说还不够吗?
  • 您希望助手做什么?哦,使用ViewBag 而不是ViewData,这是一种更好的做法。 ;)
  • @e.campver 其实我的想法正好相反。我尽可能避开动态。我喜欢我的语言是强类型的。
  • @mariozski 我有一个呈现表格的局部视图。我希望调用代码能够指示如何呈现“标题”行,因为它需要呈现两次。
  • @Jez 它仍然是严格输入的,但是是动态的。 :-) 鸭子打字和其他东西仍然是不可能的。

标签: c# asp.net-mvc razor


【解决方案1】:

DefineRenderer-方法需要返回除 void 以外的任何内容,例如IHtmlString 用剃须刀调用它 @-syntax 否则你需要这样调用它:

@{
    Html.DefineRenderer("AnalysisTableHeader", () => {
        <p>test paragraph</p>
    });
}

编辑:抱歉,我看到renderer 参数的类型是System.Action。我认为它必须是System.Func&lt;dynamic, HelperResult&gt; 类型,你需要调用它

@{ Html.DefineRenderer("AnalysisTableHeader", @<text><p>test paragraph</p></text>); }

例如。然后你可以像这样渲染它:render(null).ToHtmlString()。无论如何请注意,如果您在视图中执行此类操作,您可能会遇到部分视图缓存问题。

【讨论】:

  • 您能否详细说明部分视图缓存问题可能是什么?
  • 当然我可以:这是关于当前渲染的内容和未渲染的内容。假设您更改了 DefineRenderer 方法的签名,使其返回 IHtmlString (MvcHtmlString.Empty) 并且您在视图中调用 @Html.DefineRenderer("x", ...)。在您使用 OutputCacheAttribute 的那一刻,系统将生成的 Html 标记保存到缓存(在这种情况下为空字符串),并且稍后不会调用 DefineRenderer 方法,因为它提供缓存的结果。一旦您尝试访问渲染器,这将失败,因为它从未被放入 ViewDataDictionary。
  • 那么在这种情况下实现我想要的最佳方式是什么?
  • @Jez 这取决于您要实现的目标。我认为您应该始终在需要的地方渲染标记以避免问题和困难。你不能改用 html 助手吗?另见weblogs.asp.net/scottgu/archive/2011/05/12/…
  • 我可以,但我不是必须为我想渲染的每个不同的表格行都有一个助手吗?我认为这些信息应该在我称之为部分的视图中。
【解决方案2】:

我仍然不能 100% 确定这是否是您需要的,但也许它会有所帮助。 在局部视图中定义 @helper 并根据模型中的值决定要渲染的版本:

在代码中使用标头类型声明枚举

public enum HeaderTypes
{
    AnalysisTable,
    SomethingElse
}

那么在你看来

@helper RenderHeader(HeaderTypes headerType) {
    switch (headerType)
    {
        case HeaderTypes.AnalysisTable:
            @: <p>your html</p>
            break;
        default: 
            @: <p>default</p>
            break;
    }
}

@RenderHeader(HeaderTypes.None)
@RenderHeader(HeaderTypes.AnalysisTable)

或者您可以根据字符串值或其他内容进行切换。

【讨论】:

  • 有没有办法将辅助方法传递给局部视图,以便我的局部可以调用类似RenderHeaderRow() 的东西,并且使用局部的每个视图都必须定义一个名为RenderHeaderRow 的辅助方法?
  • 你可能会在视图中做这样的事情:@Html.Partial("PartialName", new { renderer = RenderCustom(HeaderTypes.AnalysisTable) }) 和部分:@Model.renderer。这对你来说足够了吗?为什么不在部分本身定义助手? :)
  • 因为语义上我认为在父视图本身中定义父视图中不同的标记是有意义的。但是,您的建议似乎奏效了。我现在正在开发它,可能会在您的解决方案中发布一个答案,完善。
【解决方案3】:

从@mariozski 的评论中得到一些启发,我设法获得了我想要工作的行为。我使用@helper 作为渲染器。我传递给部分的模型包含辅助渲染的结果,即。 HelperResult。所以它看起来像这样:

public class AnalysisResponseTableViewModel {
    public HelperResult HeaderTypeRowRenderer { get; set; }
    public List<AnalysisUserResponseViewModel> Responses { get; set; }
}

然后,调用视图像这样调用部分:

@helper RenderHeaderTypeRow() {
    <tr class="headerTypeRow">
        <td>Header type row</td>
        <td>Goes here</td>
    </tr>
}

@Html.Partial("AnalysisResponseTableContentsPartial",
    new AnalysisResponseTableViewModel {
        Responses = Model.OverallCaseStudyUserResponses,
        HeaderTypeRowRenderer = RenderHeaderTypeRow()
    }
)

最后,部分本身可以像这样多次渲染“标题类型行”:

@Html.Raw(Model.HeaderTypeRowRenderer.ToHtmlString())
@{bool reachedSummaryRows = false;}
@foreach (var response in Model.Responses) {
    if (!reachedSummaryRows && !response.IsPass.HasValue) {
        reachedSummaryRows = true;
        @:@Html.Raw(Model.HeaderTypeRowRenderer.ToHtmlString())
    }

    // other table rows here
}

【讨论】:

    猜你喜欢
    • 2017-06-09
    • 1970-01-01
    • 2017-11-24
    • 2013-09-25
    • 2011-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多