Rendering Main Navigation
w
Hi, I am trying to render my main navigation on the frontend, I have my code in a partial and can get the links that are under the "Home" node. I need to get any child pages of the child page under the home node. I have this so far; `@inherits Umbraco.Web.Mvc.UmbracoViewPage @{ var selection = Umbraco.ContentAtRoot().FirstOrDefault() .Descendants() .Where(x => x.IsVisible()); } @foreach (var item in selection) { @item.Name } ` Portal Login`` My content node structure looks like this https://prnt.sc/0um2Pv97wvHM I've changed the children method to descendants and that's just listing ALL links as a long list. This is what I am trying to achieve with the menu: https://prnt.sc/xwtL7-3r5k2R
h
If you're looping through the 'Children' of Home, then in your foreach loop, each Item should then expose it's Children recursively. Etc etc
w
Yes, it's bringing the children of home out - but I now want to get the children of "What we do" to show. How would I go about doing that?
h
Yes sorry that is what I mean, so in your foreach loop in your example. Something similar to this should work
Copy code
<ul id="menu-main-menu" class="menu">
        @foreach (var item in selection)
        {
            <li>
                <a href="@item.Url" title="@item.Name">@item.Name</a>

                @if(item.Children().Any())
                {
                  <ul class="submenu">
                    foreach(var subitem in item.Children())
                    {
                      <li>
                        <a href="@subitem.Url" title="@subitem.Name">@subitem.Name</a>
                      </li>
                    }
                  </ul>
                }
            </li>
        }
    </ul>
(you may wish to handle that differently but
Copy code
item.Children()
should give you the 'childrens children' of your home node
If I've understood your question correctly 🙂
w
Yeah, that makes sense @Hooky but it throws this error;
error CS0103: The name 'subitem' does not exist in the current context'
h
You're quite right, I'd forgotten the 'var' in the second foreach. I've corrected that, that is more of an example though to give you an idea
w
So the first foreach looks for the children nodes under the home node and then the 2nd loop looks for any children of the child
h
That's correct yes, and the second loop will only happen if there are any children to loop through 🙂
w
Ah, now the link that has children links, that is not showing at all on the frontend. but the other links are.
So all the pages under the Home node has a doc type of "Content Page" https://prnt.sc/edOSGUJsoAri
Now throwing an error here;
var selection = Umbraco.ContentAtRoot().FirstOrDefault().Children().Where(x => x.IsVisible());
System.NullReferenceException: 'Object reference not set to an instance of an object.'
h
All your doc types for your pages should inherit from IPublishedContent (built in to Umbraco), it's this that gives them the methods to access children etc You can try the following, of course, the html markup, classes etc would need to be amended for your project I've just added some examples in here (for example the css class 'subitem')
Copy code
<ul>
    @foreach (var item in selection)
    {
        <li>
            <a href="@item.Url" title="@item.Name">@item.Name</a>

            @if (item.Children().Any())
            {
                <ul class="submenu">
                    @foreach(var subitem in item.Children())
                    {
                        <li>
                            <a href="@subitem.Url" title="@subitem.Name">@subitem.Name</a>
                        </li>
                    }
                </ul>
            }
        </li>
    }
</ul>
Might also depend what version of Umbraco you're using. Either way, the
Copy code
.Children()
method of your items in your loop should output what you need 🙂
w
Umbraco 8
h
Should be ok then
Sorry I missed your last message regarding the new error, I'm not sure what would be null in that instance, might be worth sticking a breakpoint on and finding out what is null - that looks ok as far as I can see
w
The site won't even run locally now. https://gyazo.com/b248e613b0c51b104feab288938395c0 So just looking into this now.
h
Ah that definitely seems unrelated to any of the code above. Probably needs a restart, you can check the log files
w
Where would I find the log file on my local machine?
h
Off the top of my head, I think it should be within the project directory, App_Data > Temp > Logs
Something like that
w
Yes, found it, rebuilt the solution after cleaning it, ran it again and it's worked. I just need to build my html around the menu now 🙂
Result: https://prnt.sc/ArM02uTxSH52 Working flawlessly now.. I'll definitely save this snippet of code so I don't forget it. Thank you @Hooky
h
Awesome, glad I could help. Good luck with the project
w
Thank you, On the top LI element i Need to check if it has children pages and then append a class to it. I've done the following;
<li class="@if(item.Children().Any()){ menu-item-has-children }">
System.Web.HttpCompileException: 'D:\sites\PrincipleNetworks.Web\Views\Partials\navigation.cshtml(13): error CS1002: ; expected'
h
Copy code
@foreach (var item in selection)
            {
                var hasChildren = item.Children.Any();

                <li class="@(hasChildren == true ? "someClass" : null)">
You could do something like that (I've made 'hasChildren' a variable so that it's a little bit easier to read)
So if there are children, it will add 'someClass' if not, the class attribute will be empty
w
Bizarre - i don't see anything wrong with the code but it keeps throwing
System.Web.HttpCompileException: 'D:\sites\PrincipleNetworks.Web\Views\Partials\navigation.cshtml(5): error CS1002: ; expected'
h
What is on line 5? It wants a semicolon (;) at the end of something
w
fixed it now. 😄
h
👍
w
Phew, I think that calls for lunch time - get away from the screen and come back refocused. 🙂
m
If you wanted something to support how ever many levels you have.. then if instead of using the navigation snippet to create a partial.. use the sitemap snippet.. that has a recursive traverse pattern. The following is from v12, but think the partial snippet also exists in v8 as a starting point.
Copy code
csharp
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage
@using Umbraco.Cms.Core
@using Umbraco.Cms.Core.Models.PublishedContent
@using Umbraco.Cms.Core.Routing
@using Umbraco.Extensions

@inject IPublishedValueFallback PublishedValueFallback
@inject IPublishedUrlProvider PublishedUrlProvider
@*
    This snippet makes a list of links of all visible pages of the site, as nested unordered HTML lists.

    How it works:
    - It uses a local method called Traverse() to select and display the markup and links.
*@

@{ var selection = Model.Root(); }

<div class="sitemap">
    @* Render the sitemap by passing the root node to the traverse method, below *@
    @{ Traverse(selection); }
</div>

@* Helper method to traverse through all descendants *@
@{
    void Traverse(IPublishedContent? node)
    {
        //Update the level to reflect how deep you want the sitemap to go
        const int maxLevelForSitemap = 4;

        @* Select visible children *@
        var selection = node?.Children.Where(x => x.IsVisible(PublishedValueFallback) && x.Level <= maxLevelForSitemap).ToArray();

        @* If any items are returned, render a list *@
        if (selection?.Length > 0)
        {
            <ul>
                @foreach (var item in selection)
                {
                    <li class="level-@item.Level">
                        <a href="@item.Url(PublishedUrlProvider)">@item.Name</a>

                        @* Run the traverse method again for any child pages *@
                        @{ Traverse(item); }
                    </li>
                }
            </ul>
        }
    }
}
w
Definitely will come in handy where the nav is a little more complex - for this project, the menu is just a simple dropdown.