Razor语法 - 如何有条件地包装一些内部HTML

时间:2014-06-24 11:14:09

标签: html asp.net-mvc razor

在Razor中,有一个奇怪的规则,即只允许if块中的关闭 HTML。

请参阅:

Razor doesn't understand unclosed html tags

但我有一种情况,我想在某些条件下排除一些外包装元素。我不想重复所有内部HTML,这是相当多的HTML和逻辑。

解决这个问题的唯一方法就是让内部的东西保持另一个部分视图来保持干净吗?

对于这个新的部分没有任何其他重复使用,感觉真的很尴尬,臃肿。我想知道这条规则是Razor的限制,还是简单的纳米(烦人)功能。

4 个答案:

答案 0 :(得分:31)

您可以使用Html.Raw(mystring)。在myString中,您可以根据需要编写任何内容,例如标记打开或关闭,而不会出现任何错误。即。

if (condition) {
  @Html.Raw("<div>")
}

if (condition) {
  @Html.Raw("</div>")
}

注意:@Html.Raw("<div>")可以用更短的替代形式编写,如下所示:@:<div>

您还可以create your own html helpers来简化剃刀语法。这些帮助器可以接收条件参数,因此您可以执行以下操作:

@Html.DivOpen(condition)

@Html.DivClose(condition)

或更复杂的帮助程序,允许指定属性,标记名称等。

也不是Raw,html帮助器都不会被检测为“标签”,因此您可以自由使用它们。

最好的方法

实现像BeginForm html助手这样的东西会更安全。您可以查看源代码。它易于实现:您只需在构造函数中编写开始标记,并在Dispose方法中编写结束标记。这种技术的优点是你不会忘记关闭有条件的标签。

您需要实现此html帮助程序(在静态类中声明的扩展方法):

  public static ConditionalDiv BeginConditionalDiv(this HtmlHelper html, 
    bool condition)
  {
      ConditionalDiv cd = new ConditionalDiv(html, condition);
      if (condition) { cd.WriteStart(); }
      return cd; // The disposing will conditionally call the WriteEnd()
  }

使用这样的类:

  public class ConditionalDiv : IDisposable
  {
      private HtmlHelper Html;
      private bool _disposed;
      private TagBuilder Div;
      private bool Condition;

      public ConditionalDiv(HtmlHelper html, bool condition)
      {
          Html = html;
          Condition = condition;
          Div = new TagBuilder("div");
      }

      public void Dispose()
      {
          Dispose(true /* disposing */);
          GC.SuppressFinalize(this);
      }

      protected virtual void Dispose(bool disposing)
      {
        if (!_disposed)
        {
            _disposed = true;
            if (Condition) { WriteEnd(); }
        }
      }

       public void WriteStart()
       {
           Html.ViewContext.Writer.Write(Div.ToString(TagRenderMode.StartTag));
       }

       private void WriteEnd()
       {
           Html.ViewContext.Writer.Write(Div.ToString(TagRenderMode.EndTag));
       }
  }

您可以使用与BeginForm相同的模式。 (免责声明:此代码未经过全面测试,但可以了解其工作原理。您可以接受属性,标记名称等的额外参数。)

答案 1 :(得分:0)

使用@helper HeplerName()声明剃刀助手,在任何地方通过@HeplerName()调用。而且,您可以根据需要添加参数。

@if (condition)
{
  <div class="wrapper">
    @MyContent()
  </div>
}
else
{
  @MyContent()
}

@helper MyContent()
{
  <img src="/img1.jpg" />
}

答案 2 :(得分:0)

以下代码基于@JotaBe的答案,但经过简化和可扩展:

public static class HtmlHelperExtensions
{
    public static IDisposable BeginTag(this HtmlHelper htmlHelper, string tagName, bool condition = true, object htmlAttributes = null)
    {
        return condition ? new DisposableTagBuilder(tagName, htmlHelper.ViewContext, htmlAttributes) : null;
    }
}

public class DisposableTagBuilder : TagBuilder, IDisposable
{
    protected readonly ViewContext viewContext;
    private bool disposed;

    public DisposableTagBuilder(string tagName, ViewContext viewContext, object htmlAttributes = null) : base(tagName)
    {
        this.viewContext = viewContext;

        if (htmlAttributes != null)
        {
            this.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
        }

        this.Begin();
    }

    protected virtual void Begin()
    {
        this.viewContext.Writer.Write(this.ToString(TagRenderMode.StartTag));
    }

    protected virtual void End()
    {
        this.viewContext.Writer.Write(this.ToString(TagRenderMode.EndTag));
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            this.disposed = true;

            this.End();
        }
    }
}

可以在Razor视图中以下列方式使用它:

@using (Html.BeginTag("div"))
{
    <p>This paragraph is rendered within a div</p>
}

@using (Html.BeginTag("div", false))
{
    <p>This paragraph is rendered without the div</p>
}

@using (Html.BeginTag("a", htmlAttributes: new { href = "#", @class = "button" }))
{
    <span>And a lot more is possible!</span>
}

答案 3 :(得分:0)

这是IMO实现此目的的最便捷方法:

[HtmlTargetElement(Attributes = RenderAttributeName)]
public class RenderTagHelper : TagHelper
{
    private const string RenderAttributeName = "render";
    private const string IncludeContentAttributeName = "include-content";

    [HtmlAttributeName(RenderAttributeName)]
    public bool Render { get; set; }

    [HtmlAttributeName(IncludeContentAttributeName)]
    public bool IncludeContent { get; set; }

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        if (!Render)
        {
            if (IncludeContent)
                output.SuppressOutput();
            else
                output.TagName = null;
        }

        output.Attributes.RemoveAll(RenderAttributeName);
        output.Attributes.RemoveAll(IncludeContentAttributeName);
    }
} 

然后,您就可以像这样使用它:

<div render="[bool-value]" include-content="[bool-value]">
    ...
</div>