Is there a way to make a @section optional with the asp.net mvc Razor ViewEngine?

一曲冷凌霜 提交于 2019-12-04 04:59:09

I ended up doing something a little hacky to get it working how I needed it.

on my page i have:

@{
    Layout = "../Shared/Layouts/_Layout.cshtml";
    var mycollection = (ViewBag.TheCollection as IQueryable<MyCollectionType>);
    ViewBag.ShowContentRight = mycollection != null && mycollection.Count() > 0;
}

then in my layout i have:

@if(IsSectionDefined("ContentRight") && (ViewBag.ShowContentRight == null ||ViewBag.ShowContentRight == true)) 
{
    <div class="right">
        RenderSection("ContentRight")
    </div>
}
else if(IsSectionDefined("ContentRight"))
{
    RenderSection("ContentRight")
}

If the section is defined it has to be rendered, but if there's no content i dont want the <div>s

If there's a better way i'd like to know.

The renderer is expecting the method to be called sometime in the layout file. You can spoof the renderer and use "global" conditionals (think login).

@{
    ViewBag.content = RenderBody();
}
@if (Request.IsAuthenticated) {
        @ViewBag.content;
} 
else {
        @Html.Partial("_LoginPartial")
}

Extension method with private static readonly field info for perf:

private static readonly FieldInfo RenderedSectionsFieldInfo = typeof(WebPageBase).GetField("_renderedSections", BindingFlags.Instance | BindingFlags.NonPublic);

public static void EnsureSectionsAreRegisteredAsRendered(this WebPageBase webPageBase, params string[] sectionNames)
{
    var renderedSections = RenderedSectionsFieldInfo.GetValue(webPageBase) as HashSet<string>;
    if (renderedSections == null)
    {
        throw new WebCoreException("Could not get hashset from private field _renderedSections from WebPageBase");    
    }
    foreach (var sectionName in sectionNames)
    {
        if (!renderedSections.Contains(sectionName))
        {
            renderedSections.Add(sectionName);
        }
    }
}

In your cshtml:

@{ this.EnsureSectionsAreRegisteredAsRendered("SectionName1", " SectionName2", "…"); }

Yes, yes, yes.... I know.... bad reflection! Use at your own risk :)

I use the following method in my view base class (from this excellent blog post http://haacked.com/archive/2011/03/05/defining-default-content-for-a-razor-layout-section.aspx/):

public HelperResult RenderSection(string name, Func<dynamic, HelperResult> defaultContents)
{
    if (IsSectionDefined(name))
    {
        return RenderSection(name);
    }
    return defaultContents(null);
}

If you don't have a view base class, I recommend one because it lets you add all sorts of little extra functionality to your views. Just create a class with the following signature: public abstract class MyViewPage<T> : WebViewPage<T> and then set it in your web.config:

<system.web.webPages.razor>
  <pages pageBaseType="MyViewPage">
    ...
  </pages>
</system.web.webPages.razor>

You can wrap your whole section in an if statement with IsSectionDefined

Layout.cshtml:

@if (IsSectionDefined("ContentRight"))
{
    <div>
    @RenderSection(name: "ContentRight", required: false)
    </div>
}

Your cshtml page:

@section ContentRight
{    
    @if (mycollection != null && mycollection.Count() > 0)
    {   
    <h2>
        Stuff
    </h2>
    <ul class="stuff">
        @foreach (MyCollectionType item in mycollection )
        {
            <li class="stuff-item">@item.Name</li>
        }
    </ul>
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!