Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Robert Valcourt 70 posts 103 karma points
    Sep 09, 2015 @ 16:41
    Robert Valcourt
    0

    Razor subnavigation that shows on child pages

    I'm trying to build a sub navigation partial view. At present i'm using the built in script:

    @inherits Umbraco.Web.Mvc.UmbracoTemplatePage
    @{ var selection = CurrentPage.Children.Where("Visible"); }
    
    @if (selection.Any())
    {
    <h4>In This Section</h4>
    <ul>
        @foreach (var item in selection)
        {
            <li>
                <a href="@item.Url">@item.Name</a>
            </li>
        }
    </ul>
    }
    

    This script works fine only when you are viewing the parent node. Lets assume the following node structure:

    /gatewaypage/
      /childpage1/
      /childpage2/
      /childpage3/
    

    So when viewing /gatewaypage/ a list of all child pages are displayed. The problem is when you then navigate to one of the child pages the sub menu is no longer displayed because the script checks for CurrentPage.Children and child pages don't have child pages of their own.

    How can the script be modified so the gateway page and all child pages show the same menu? In XSLT I do this:

    <xsl:variable name="level" select="2"/>
    <xsl:variable name="mainNodes" select="$currentPage/ancestor-or-self::node()[@isDoc and @level=$level]/node()"/>
    
    <xsl:if test="count($mainNodes) > 0">
    <ul>
      <xsl:for-each select="$mainNodes">
      <li>
          <a href="{umbraco.library:NiceUrl(@id)}"><xsl:value-of select="@nodeName"/></a>
      </li>
      </xsl:for-each>
    </ul>
    </xsl:if>
    

    I'm new to Razor and am uncertain of the equivalent syntax. Many thanks.

  • jivan thapa 193 posts 658 karma points
    Sep 09, 2015 @ 18:20
    jivan thapa
    0

    You should understand the basic concept of CurrentPage. when you say children of the current page It means the page you are visiting now. E.g childpage 1, childpage2, and childpage 3 are children of "gatewaypage". So If you visit the page "gatewaypage". and childpage1,2,3 are children.

    If you visit the page "childpage1". there is no children of "childpage1". therefore currentPage.Children returns empty results.

    You need to modify your logic. Have a look at the sample code.

    @{
        var currentPage = (IPublishedContent) CurrentPage;
    
        var selection = currentPage.Children.Where("Visible");
    
        if (currentPage.Level == 2) // assume that gatewaypage's level = 1, and childpage's level = 2.
        {
            selection = currentPage.AncestorOrSelf(1).Children.Where("Visible");
        }
    }
    
    @if (selection.Any())
    {
    <h4>In This Section</h4>
    <ul>
        @foreach (var item in selection)
        {
            <li>
                <a href="@item.Url">@item.Name</a>
            </li>
        }
    </ul>
    }
    
  • Openfield Creative 62 posts 222 karma points
    Sep 09, 2015 @ 18:26
    Openfield Creative
    0

    Hi Robert,

    You'll want to change

    @{ var selection = CurrentPage.Children.Where("Visible"); }
    

    to

    @{ var selection = CurrentPage.AncestorOrSelf(1).Children.Where("Visible"); }
    

    to get the children of the root. If you want a submenu just for an individual page you'll need to change the 1 to reflect the level of the gateway page.

    Hope this helps.

    Owen

  • Robert Valcourt 70 posts 103 karma points
    Sep 09, 2015 @ 18:42
    Robert Valcourt
    0

    Jivan & Owen,

    Thank you for the replies. I do have a complete understanding of currentPage, and I understand why the default script failed. What I couldn't figure out was the sytnax for the Ancestor and level.

    I can write any function I want in XSLT but am still learning the Razor equivalent syntax.

    Owen's example worked perfectly.

    Now, each child node has a text field with the alias of 'subNavText'. I would like that value used ... IF defined ... and only use the @nodeName if left empty.

    In Razor the link text value is defined as @item.Name. How do I go about adding the logic:

    if 'subNavText' is defined, use the value instead of @item.Name. Otherwise use @item.Name. In XSLT this is easy using an xsl:choose.

  • Openfield Creative 62 posts 222 karma points
    Sep 09, 2015 @ 18:47
    Openfield Creative
    0

    For this you'll want to use HasValue()

    if(item.HasValue("subNavText"))
    {
        @item.GetPropertyValue("subNavText")}
    else
    {
        @item.Name
    }
    
  • Openfield Creative 62 posts 222 karma points
    Sep 09, 2015 @ 18:52
    Openfield Creative
    1

    You may also want to check out this Razor Cheat Sheet.

    https://our.umbraco.org/projects/developer-tools/umbraco-v6-mvc-razor-cheatsheets

    It was originally created for Umbraco V6 but it still holds true for V7. It's what helped me to really get a good grasp of Razor and I still reference it often.

  • Robert Valcourt 70 posts 103 karma points
    Sep 09, 2015 @ 22:09
    Robert Valcourt
    0

    Thanks Owen, works great.

    Off topic, maybe you have some thoughts on the following:

    One of my pages has a content picker. Lets alias it 'customPageID'

    Trying to use Razor, I'd like to create a URL out of it when used.

    @inherits Umbraco.Web.Mvc.UmbracoTemplatePage
    @{
        if (CurrentPage.HasValue("customPageID"))
        {
            <link rel="canonical" href="@Umbraco.Field("customPageID").UrlWithDomain" />
        }
    }
    

    Yes, I know that syntax is wrong but you have the idea. Trying to make Razor get the ID value from the currentPage content picker and have it output a full URL with domain. I've tried a dozen examples but they all fail.

    Update. I found a script that seems to work, though it seems to be based off a different schema:

    if (Model.Content.HasValue("metaCanonical")){
        var node = Umbraco.TypedContent(Model.Content.GetPropertyValue<int>("metaCanonical"));
        <link rel="canonical" href="http://@HttpContext.Current.Request.Url.Host@node.Url" />
    }
    

    I'm struggling to understand the difference between the Dynamic and Typed Razor syntax and which is best to use. I thought I read somewhere that one of the methods is due to be depreciated (or may already be?). Often the examples of Razor I find online are one or the other and I try to blend them with other scripts that are incompatible. It's not always clear to me whether its dynamic or typed.

    Reminds me of when I used to use $currentPage/data[@alias='someValue'] vs $currentPage/someValue in XSLT.

  • Openfield Creative 62 posts 222 karma points
    Sep 11, 2015 @ 20:03
    Openfield Creative
    0

    Sorry for the delay in a reply.

    If you wanted use dynamics for this it would probably look something like:

    if(CurrentPage.HasValue("metaCanonical"))
    {
         var node = Umbraco.Content(CurrentPage.MetaCanonical);
         <link rel="canonical" href="http://@HttpContext.Current.Request.Url.Host@node.Url"
    }
    

    As for whether to use Dynamic or Typed syntax, it seems to me that it's mostly a personal preference. I tend to mix the two within my projects, using Dynamic for templating and Typed for my partial views. I'm sure the community has a lot of opinions on this, and I by no means think this is the correct/best way to go.

Please Sign in or register to post replies

Write your reply to:

Draft