Copied to clipboard

Flag this post as spam?

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


  • Matt 353 posts 825 karma points
    Jan 28, 2020 @ 08:11
    Matt
    0

    Get children of children.

    Hi all,

    I'm trying to create a side nav for my site as the structure has alot of pages within pages.

    I want this side nav to be dynamic so it can render the children of children no matter what node is selected.

    My structure will have this many layers.

    enter image description here

    Here is my code so far, which shows the Department, Wards, but I want to get the Departments which sit under those.

    @inherits Umbraco.Web.Mvc.UmbracoViewPage
    @using Umbraco.Web;
    
    
    @{
        var siteSection = Model.AncestorOrSelf(2);
        var selection = siteSection.Children.Where(x => x.IsVisible());
    }
    
    @foreach (var item in selection)
    {
        <a class="nav-link nav-link--black nav-link--air-bottom @(item.IsAncestorOrSelf(Model) ? "nav-link--active" : null)" href="@item.Url">@item.Name</a>
    }
    

    Hope I've made that clear.

    Thanks in advance

    Matt

  • Sebastian Dammark 581 posts 1385 karma points
    Jan 28, 2020 @ 08:45
    Sebastian Dammark
    1

    If you want them as a huge list

    var selection = siteSection.Descendants().Where(x => x.IsVisible()); 
    

    Or if they have to be ordered in some way

    @{
    var siteSection = Model.AncestorOrSelf(2);
    var selection = siteSection.Children.Where(x => x.IsVisible());
    
    if(selection.Any()) {
        <ul>
            @foreach (var item in selection) {
                @link(item)
            }
        </ul>
      }
    }
    
    @helper link(IPublishedContent item) {
       var children = item.Children.Where(x => x.IsVisible());
       <li>
           <a class="nav-link nav-link--black nav-link--air-bottom @(item.IsAncestorOrSelf(Model) ? "nav-link--active" : null)" href="@item.Url">@item.Name</a>
             @if(children.Any()) {
               <ul>
                @foreach (var item in selection) {
                    @link(item)
                }
               </ul>
           }
       </li>
     }
    

    The code is untested, so there might be a few issues ;)

  • Matt 353 posts 825 karma points
    Jan 28, 2020 @ 09:05
    Matt
    0

    Hi Sebastian,

    Thanks for getting back to me. as you've mentioned im getting an error;

    Compiler Error Message: CS0103: The name 'selection' does not exist in the current context

    Source Error:

    Line 26: { Line 27:

      Line 28:
      @foreach (var item in selection)

    Are you able to point me in right direction?

    Thanks

    Matt

  • Sebastian Dammark 581 posts 1385 karma points
    Jan 28, 2020 @ 09:32
    Sebastian Dammark
    0

    Yeah, let's fix that :)

    In line 28, just replace selection with children

  • Matt 353 posts 825 karma points
    Jan 28, 2020 @ 10:13
    Matt
    0

    Cheers Sebastian.

    Sorry another I cant figure out;

    The name 'selection' does not exist in the current context

    I tried adding;

    var selection = siteSection.Children.Where(x => x.IsVisible());

    above for the foreach loop but no joy.

    Thanks

  • Sebastian Dammark 581 posts 1385 karma points
    Jan 28, 2020 @ 10:21
    Sebastian Dammark
    0

    Nope.

    In this line @foreach (var item in selection) replace selection with children.

    According to your code it should be in line 28.

  • Matt 353 posts 825 karma points
    Jan 28, 2020 @ 10:59
    Matt
    0

    Thanks Sebastian!

    Thats working a treat, 1 final thing is it possible to exclude the next layer?

    At the moment its displaying children of children of children see below;

    enter image description here

    How can I get it to ignore the about the team, support and advice etc? I know I could use the Umbraco Navi Hide, although I dont want to hide them as these pages are displayed on its own menu within each department.

    Thanks

  • Sebastian Dammark 581 posts 1385 karma points
    Jan 28, 2020 @ 13:21
    Sebastian Dammark
    1

    You could set a max level like this

    @{
        var maxLevel = 3;
        var siteSection = Model.AncestorOrSelf(2);
        var selection = siteSection.Children.Where(x => x.IsVisible() && x.Level <= maxLevel);
    
        if(selection.Any()) {
            <ul>
                @foreach (var item in selection) {
                    @link(item, maxLevel)
                }
            </ul>
        }
    }
    
    @helper link(IPublishedContent item, Integer level) {
        var children = item.Children.Where(x => x.IsVisible() && x.Level <= level);
        <li>
            <a class="nav-link nav-link--black nav-link--air-bottom @(item.IsAncestorOrSelf(Model) ? "nav-link--active" : null)" href="@item.Url">@item.Name</a>
            @if(children.Any()) {
                <ul>
                    @foreach (var item in children) {
                        @link(item, level)
                    }
                </ul>
            }
        </li>
     }
    

    If you wanna exclude a specific page you can do in multiple ways. You can do it by the DocumentType, by it's position or by a specific property.

    By Document Type: Model.ContentType.Alias By position: Model.FirstOrDefault() By a specific property: Model.Value({propertyName})

    It depends a lot on the situation.

  • Matt 353 posts 825 karma points
    Jan 28, 2020 @ 14:04
    Matt
    0

    Thanks for reply, appreciate you taking the time to help.

    Basically I'm looking at creating sub menu on the left like this;

    enter image description here

    Basically have a menu at the top called e.g Menu 1,2,3 etc, you click into this and it loads the Menu 1 page up with list of all the nodes on the left hand side under Menu 1. I would then look to only list 1 child under those.

    Hope that makes better sense.

    I tried your updated code but getting

    CS0246: The type or namespace name 'Integer' could not be found (are you missing a using directive or an assembly reference?)

    Tried adding @using System.Numerics; but no change.

    Thanks again.

    Matt

  • Lander Debeuf 23 posts 125 karma points
    Jan 29, 2020 @ 13:38
    Lander Debeuf
    0

    Matt,

    If i understand your question correctly, you want all children no matter what level they are on of the type Departments?

    @{    
        var rootNode = Model.Root();
        var myDepartmentNodes = rootNode.Descendants<Department>().Where(x => x.IsVisible());
    }
    

    then you can iterate again lke you are doing. i hope this is what you are looking for

  • Matt 353 posts 825 karma points
    Jan 29, 2020 @ 13:43
    Matt
    0

    Hi Lander,

    Thanks for your reply.

    It needs to be dynamic for all nodes, so not just for Departments, so if the node has children it will display and if that node has children they will display.

    However not all nodes will have children so it will need to be clever and pick that up.

    The script by Seb is what I'm looking for, although I just need it to only display 3 levels.

    Thanks

  • Lander Debeuf 23 posts 125 karma points
    Jan 29, 2020 @ 14:19
    Lander Debeuf
    1
    var rootNode = Model.Root();
    var myDepartmentNodes = rootNode.Descendants().Where(x => x.IsVisible() && x.Level < 3);
    

    This is the easy way to get the list of all types up to level 3, if you really would like to have this done like that, i would advise you to do it different,

    Create a surfacecontroller for that page, and make a viewmodel like this,

    public class NavigationLinkModel
    {
        public string Icon { get; set; }
        public string Text { get; set; }
        public string Url { get; set; }
        public bool NewWindow { get; set; }
        public string Target { get { return NewWindow ? "_blank" : null; } }
        public string Title { get; set; }
    
        public NavigationLinkModel()
        { }
    
        public NavigationLinkModel(string url, string icon = null, string text = null, bool newWindow = false, string title = null)
        {
            Text = text;
            Icon = icon;
            Url = url;
            NewWindow = NewWindow;
            Title = title;
        } 
    
    public class NavigationListModel
    {
        public NavigationLinkModel Link { get; set; }
        public List<NavigationListModel> Items { get; set; }
        public bool HasChildren
        {
            get
            {
                return Items != null && Items.Any() && Items.Count > 0;
            }
        }
    
        public NavigationListModel()
        { }
    
        public NavigationListModel(NavigationLinkModel link)
        {
            Link = link;
        }
    }
    

    Then in the controller you have a function, (you can extend it to stop at a certain level if you like)

        private List<NavigationListModel> GetChildNavigationList(IPublishedContent page)
        {
            List<NavigationListModel> listItems = null;
    
            IEnumerable<IPublishedContent> t_childPages = (IEnumerable<IPublishedContent>)page.Children;
    
            // Getting all the child nodes that are pages with a hideFromMenu property (meaning they have the possibility to be shown as a menu item
            var childPages = t_childPages.Where(x => x.Level <= 3 && (x.HasValue("hideFromMenu") && x.GetProperty("hideFromMenu").Value<bool>() == false));
    
            if (childPages != null && childPages.Any() && childPages.Count() > 0)
            {
                listItems = new List<NavigationListModel>();
    
                foreach (var childPage in childPages)
                {                    
                    NavigationListModel listItem = new NavigationListModel(new NavigationLinkModel(childPage.Url, childPage.GetProperty("menuIcon")?.Value<string>(), childPage.Name));
    
                    // If the child pages should be rendered as a menu or just as a plain top level icon in the menu
                    if (childPage.HasProperty("dontShowAsList") && childPage.GetProperty("dontShowAsList").Value<bool>() == false)
                    {
                        listItem.Items = GetChildNavigationList(childPage);
                    }
    
                    listItems.Add(listItem);
                }
            }
    
            return listItems;
        }
    

    Then you have a nice model to pass to your view, to just render it out, it keeps the CSHTML file clear of all the code.

  • Rabin Koirala 6 posts 77 karma points
    Aug 03, 2022 @ 11:26
    Rabin Koirala
    1
    var cmsHomePage   = UmbracoContext.ContentCache.GetByXPath("//pages").First();
    

    Reference from https://our.umbraco.com/forum/umbraco-8/97508-get-content-of-a-particular-document-type

Please Sign in or register to post replies

Write your reply to:

Draft