Copied to clipboard

Flag this post as spam?

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


  • Brett Fullam 119 posts 629 karma points
    May 15, 2017 @ 14:02
    Brett Fullam
    0

    How to add anchor links to dynamic navigation?

    Can anyone recommend a solution to add achor links into a dynamically created navigation?

    I have the dynamic navigation all worked out ... so no issues there ... what I can't figure out is how to include an anchor tag (dynamically) to call the same page content.

    For example:

    Page --> Bicycle

    Specific Content Section on "Bicycle" page --> "Bike Path Information"

    How can I create a link to this internal content without duplicating content in the node tree ...? I just can't seem to wrap my head around this. If I add another doc type/child page ... it creates duplicate content in the back office. Is there a way to add the anchor tag "option" for nav items as a custom datatype?

    The page is dynamically built using a partial view for the dynamic nav, and uses Parent and child doc types for page content. The navigation is dynamically built by reading the parent-child relationships in the node tree.

    Any help or additional references would be greatly appreciated.

  • Ian 178 posts 752 karma points
    May 15, 2017 @ 22:03
    Ian
    0

    If I understand correctly you could create a document type which has just one property on it, a link picker data type which allows the specifcation of an anchor alongside the url. You would insert instances of this document type into the content tree in the back office, giving you full control over the position of these anchor links in your hierarchy.

    The action for your navigation partial view would use the relationships you have already but when it encounters a document node of the type you just created it would use the property value, using the document title as the link label and anchor information instead of the umbraco url of the node itself. When processed in your partial view it would also be best if these node links have a no follow attribute applied to aid googles treatment of many links pointing to the same page.

  • Brett Fullam 119 posts 629 karma points
    May 16, 2017 @ 18:16
    Brett Fullam
    0

    Hey Ian ... definitely helped. I can now add items to the dynamic menu, but I'm not sure it's working properly. When I click on the menu item it's just loading a blank page, and not the page it pointing to?

    What am I missing?

    When I go to view the source code of the loaded page the browser it's completely blank? There's nothing there.

    I can get header and footer content to appear if I associate the template for the Anchor Links doc type to the Master template, but that's all that appears.

    Here are some screen shots of the content tree Page, Doc Types and Templates so you can see how I've applied the doc type with the link picker data type. Right now I'm just trying to point it at an internal page to get it working prior to adding the anchor to the link. enter image description here enter image description here enter image description here enter image description here

    Thanks again for your help ... I really appreciate your input.

  • Ian 178 posts 752 karma points
    May 16, 2017 @ 22:07
    Ian
    0

    Is it possible to see some of the code you are using to render these new menu items in your menu?

    Im conceptually assuming from your original outline that there is your main page with a link in your menu and then you have these extra links also in the menu mavigating to amchors in that page.

    The main page which is what is going to be routable by umbraco and supply your rich page content is going to have to be in your content tree but just so you know neednt neccesarily appear in your menu. You could have an umbraconavihide property and then have your menu renderer check if the node should or should not appear in your menu by do isVisible or something like that i cant quite remember..

    Anyway seeing some of your menu code will help to advise you further.

  • Ian 178 posts 752 karma points
    May 16, 2017 @ 22:18
    Ian
    0

    I should also say with the direction i have suggested you wont need a template for the anchor links document type. As it is not intended that these nodes be rendered for their content only processed by your menu renderer for creating links with anchors.

  • Brett Fullam 119 posts 629 karma points
    May 17, 2017 @ 14:39
    Brett Fullam
    0

    Hi Ian,

    Really appreciate your help ... here's the partial view I'm using to generate the dynamic navigation:

    @inherits Umbraco.Web.Mvc.UmbracoTemplatePage
    
    @*
        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 custom Razor helper called Traverse() to select and display the markup and links.
    *@
    
    @{ var selection = CurrentPage.Site(); 
    
        var isHome = true;
    
    }
    
    <nav class="nav navbar navbar-default" data-spy="affix" data-offset-top="197" >
    <div class="container navWrap col-centered">
    
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
        </div>
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
        <ul class="nav navbar-nav">
        @* Render the sitemap by passing the root node to the traverse helper, below *@
        @Traverse(selection, isHome)
        </ul>
            <form class="navbar-form navbar-right">
            <div class="form-group has-feedback">
              <input type="text" class="form-control" placeholder="Pesquisar"><i class="form-control-feedback glyphicon glyphicon-search"></i>
            </div>
            <!-- REMOVE BUTTON <button type="submit" class="btn btn-default">Enviar</button> -->
          </form>
        </div>
    </div>
    </nav>
    
    
    @* Helper method to travers through all descendants *@
    @helper Traverse(dynamic node, bool isHome)
    {
        @* Update the level to reflect how deep you want the sitemap to go *@
        var maxLevelForSitemap = 3;
    
        @* Select visible children *@
        var selection = node.Children.Where("Visible").Where("Level <= " + maxLevelForSitemap);
    
        @* If any items are returned, render a list *@
        if (selection.Any())
        {
    
    
                if(isHome) {
                                var cssClass = CurrentPage.Site().Id == CurrentPage.Id ? "active" : null;
                                isHome = false;
                            }
                foreach (var childPage in selection.Where("Visible"))
                            {   
                                if (childPage.Children.Any())
                                {                    
                                    <li class="has-child @(childPage.IsAncestorOrSelf(CurrentPage) ? "selected" : null)">
                                    <a class="menuHeader dropdown-toggle menuTextMain" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" href="#">@childPage.Name</a>
    
    
                                <ul class="dropdown-menu navItems">
                                    <li class="dropdown">
                                        @* Run the traverse helper again for any child pages *@
                                        @Traverse(childPage, isHome)
                                    </li>
                                </ul>
                                </li>
                                }
                                else
                                {
                                    <li class="@(childPage.IsAncestorOrSelf(CurrentPage) ? "selected" : null)">
                                    <a class="menuTextMain" href="@childPage.Url">@childPage.Name</a>
                                    </li>
                                }            
    
    
                            }
    
                }
    }
    

    I'm also using Umbraco v7.6 ... and I'm using a "Related Links" property in the document type "Achor Links". It could be the "@childPage.Url" is where this is falling short ... because this would effectively link it to the blank Anchor Links page and not the link listed in the "related links" picker, right?

    Let me know if there's any additional information you'd like to see.

  • Ian 178 posts 752 karma points
    May 17, 2017 @ 17:24
    Ian
    0

    Yes as you say always using the url property is where its going wrong. Instead you will have to have a condition where you do this for normal content nodes but if it is an anchor doctype get the data type property value and process that instead to form your url.

    Im not using umbraco any more at the moment, but when i did, i like nicholas used archtype for a menu system on one project, it was a pretty flexible approach though a colleague of mine didnt like it at all due to the extra level of admin it involved.

  • Nicholas Westby 2054 posts 7100 karma points c-trib
    May 17, 2017 @ 18:34
    Nicholas Westby
    0

    a colleague of mine didnt like it at all due to the extra level of admin it involved

    FYI, one of the ways I reduce the level of admin in Archetype-based navigation is to add two rather than one fieldset type. That is, in addition to "Menu Item", I add "Automatic Menu Item".

    In "Automatic Menu Item", I choose a parent page, and each child page gets automatically converted into a menu item. Depending on the specific requirements, you can add extra customizations (e.g., typing in a document fragment to suffix to each automatic menu item).

    This is nice because it gives you the ability to customize the menu, but you can also automate portions of it to reduce the content entry burden.

  • Brett Fullam 119 posts 629 karma points
    May 17, 2017 @ 18:40
    Brett Fullam
    0

    Hi Nicholas,

    I'm exploring this option now ... appreciate the additional input.

    -- Brett

  • Ian 178 posts 752 karma points
    May 17, 2017 @ 19:03
    Ian
    0

    Yes i think thats what i did. The issue seemed to be that you used an additional mechanism for building up your menu when the content tree was there and appeared to reflect the nav heirachy anyway. When using other systems back in the day (cough wordpress) i also experienced the limitations of nav directly built from content structure so by using archetype i was thinking to futureproof for other eventualities but hey. After that several projects were developed with no issues without using it, but i just wanted to put a balanced view of the options and illustrate how they might work.

  • Brett Fullam 119 posts 629 karma points
    May 17, 2017 @ 19:26
    Brett Fullam
    0

    Well ... this is how you learn, right?

    I think I've rebuilt the CMS 3 times now ... each time refining how I approach the interaction between the back office and how dynamic content (navigation in this example) get's created-managed. My client needs this to be dead-simple to mangage-update ... I imagine this will be the future work of interns and other office staffers.

    For the topic of discussion here regarding Archetype ... prior to trashing and rebuilding how the nav is constructed ha ha ha ... with Archetype, will I be creating a document type to manage the group of links in each nav dropdown? Is that roughly the idea here?

    You guys have a been a HUGE help. Thank you for responding so quickly and helping work though this.

  • Ian 178 posts 752 karma points
    May 17, 2017 @ 20:19
    Ian
    0

    Nicholas is the best to step in here. But with archtype it isnt necessary to create document types to represent or display management fields for navigation nodes. Instead you create archtype (or you did when i used it) datatypes these are configured with the required input fields. I think roughly i had a menu archetype and a menuitem archetype specified the first as a container for the second. I put the archtype as a property on a content node either home or a settings node i had. Archtype then produced drag and drop repeatable fields for the editing experience in the context of a document type tab and the configuration was stored by archetype as a json blob. Archetype had helpers to access nested fieldsets in which you used in a recursive manner to render.

  • Ian 178 posts 752 karma points
    May 17, 2017 @ 20:43
    Ian
    0

    My colleage also mentioned using another similar product more recently.to me called stacked content though i have never used it.

    I know it too many things to can get confusing but one consideration you might want to think about at this stage though im not saying you HAVE to is whether you plan to use grids. I am not suggesting menus should be placed in grids but what i am saying is that at some stage you might want to have editors in the grid which you create for your admins which require a collection of fields and you dont want to code the grid editor from scratch yourself using angular js. This is where nested content and as i say stacked content packages gained more traction than archetype as the editing experience could be more unified than by using archetype for one task and dynamic field editors based on document types in the grid. Archetype may have addressed this i dont know but do try to consider limiting the number of different dependencies on your site

  • Brett Fullam 119 posts 629 karma points
    May 17, 2017 @ 21:02
    Brett Fullam
    0

    Thanks for the heads up. I'm still working through this switch over to Archetype to test it out.

    In retrospect ... abandoning the dynamic menu creation I had set up actually resolves some other issues it was creating for me when I incorporated other pages with dynamic content managed via a parent-child relationship. I was wrestling with those due to the constraints my nav was creating ... now I have other options available to me for those as well.

    SO ... back to the drawing board ... learned a lot from this. Thanks Ian and Nicholas. I really apprecaited you help.

  • Nicholas Westby 2054 posts 7100 karma points c-trib
    May 17, 2017 @ 21:48
    Nicholas Westby
    0

    Yeah, that's pretty much what I do. I create a document type with a property of the Archetype data type for the menu. I then store that in a settings area of the content tree, then I pick that node from a property on the homepage. I have used other approaches, but this seems to work well. Here is an example of that the content node looks like in the back office:

    Menu Back Office

    And on the frontend it looks like this:

    Menu Frontend

    In this case, "Menu List" is the same as the "Automatic Menu Item" I mentioned.

  • Brett Fullam 119 posts 629 karma points
    May 18, 2017 @ 12:46
    Brett Fullam
    0

    Thanks Nicholas.

    It's much clearer now ... create a doc type with a property of Archetype data type for the menu ... really helpful.

    I'm working on this now ... I'll check back in with the results.

    Appreciate the visual reference extremely helpful.

  • Brett Fullam 119 posts 629 karma points
    May 18, 2017 @ 19:09
    Brett Fullam
    0

    Hi Nicholas,

    Your example, and the addition of Archetype, have definitely resovled several issues I was wrestling with regarding dynamic repeated content. Which is a HUGE win for me.

    However, while I can create custom archetypes and get their content rendered into an existing layout exactly how and where I want it ... the part about building a nav that can appear on all pages but be managed by a single instance still escapes me. So far the method I have been able to use (see below) successfully only allows a per page use, and would have to be included-managed on every doctype in the site.

    This is what I understand-implemented so far ...

    (1) Create Archetype ( Developer > Data Types > Custom Archetype )

    (2) Create custom doc type for the new Archetype instance ( Settings > Document Types > Create Document Type Without Template )

    (3) Include-add this instance to another doc type via "compositions" ( Document Types > Sample Page Doc Type > Compositions > Add Custom Archetype )

    (4) Add content in the page with the new tab containing the Archetype fieldsets ... which are then added to the page upon publishing via a partial view that's called-rendered in the page template.
    ( Settings > Partial Views > Create Custom Partial View to call-render all Archetype field sets in a foreach loop and add-render in Sample Page's Template ) ( Content > Sample Page Content > Add Archetype Content As Needed > Publish )

    NOW ... that being said ... two things I still can't quite figure out:

    (1) I didn't see anything other than a "Related Links" datatype to create the drop down child link elements ... no multinode treepicker available (v7.6 issue?). What do you recommend using here to create the list of the child pages?

    (2) How can you get this new menu to appear on every page like I accomplished with a partial view that was included in the master template?

    Any input on this would be appreciated ... thanks again for your help. Archetype is already paying off in other areas of my site.

  • Nicholas Westby 2054 posts 7100 karma points c-trib
    May 18, 2017 @ 22:10
    Nicholas Westby
    0

    I didn't see anything other than a "Related Links" datatype to create the drop down child link elements ... no multinode treepicker available

    You'll need to first create the data type based on the multinode treepicker property editor. Some property editors (like multinode treepicker) require you to create them as data types first so you can set appropriate configuration values (e.g., the start node, among others, in the case of the MNTP).

    How can you get this new menu to appear on every page like I accomplished with a partial view that was included in the master template?

    Rather than trying to extract it from the current page, get it from another page. You could do that like this (consider this pseudo code):

    var menuData =
        // Current page.
        Model.Content
        // Find homepage.
        .AncestorOrSelf("homepage")
        // Get picked menu content node from homepage property.
        .GetPropertyValue<IPublishedContent>("mainMenu")
        // Get menu (the Archetype property on the menu content node).
        .GetPropertyValue<ArchetypeModel>("menu");
    

    Here's the MNTP property on my homepage:

    MNTP on Homepage

    Here's the Archetype on the picked menu node:

    Archetype Menu

  • Brett Fullam 119 posts 629 karma points
    May 21, 2017 @ 14:53
    Brett Fullam
    0

    Hi Nicholas,

    It's amazing how taking a break and coming back fresh can help you see things differently. I just wrapped my head around the pseudo code in your last post. I'm going to try an implement this tonight.

    Archetype has been a lifesaver ... it resolved-streamlined a few sections of my site. The only thing that I find disappointing is the inability to render grid content inside of an archetype. You can put it in there but nothing gets rendered.

    I did see LeBlender for making grids more manageable ... looking into that package too.

    Thanks again for helping out. I really appreciate it.

    -- Brett

  • Brett Fullam 119 posts 629 karma points
    May 24, 2017 @ 20:59
    Brett Fullam
    0

    Hi Nicholas,

    Quick question regarding your pseudo code ... what if the archetype property is stored on a page in the content tree and not in a separate document in a settings area?

    For example ... I'm trying to render an archetype property that's currently on another page to reduce the amount of overhead in the content editor when editing common section headers across childpages in an entire section.

    Here's the content tree for the section titled "Guia do Usuário" which has several childpages mixed in with some of those anchor link placeholders (which works perfectly now). enter image description here

    To access the archetype property editor

    var archetypeData =

    // Current page.
    Model.Content
    

    No problem so far ...

    // Find Guia do Usuário page
    .AncestorOrSelf("guiaDoUsuario")
    

    Found the guiaDoUsuario page ...

    // Get picked menu content node from homepage property.
    .GetPropertyValue<IPublishedContent>(".... ? ....")
    

    This step has me a little confused ... if I'm not referencing a node other than the "guia do usuario" page ... what should I use here?

    // Get menu (the Archetype property on the menu content node).
    .GetPropertyValue<ArchetypeModel>("sectionHeader");
    

    This last step is clear enough ... and then I need to use a foreach statment like this to access the fieldsets in the archtype, right?

    foreach(var fieldset in Model.Content.GetProperty<ArchetypeModel>("sectionHeader"))
    {
    

    Archetype has been a really big help ... I can't thank you enough for bringing it to my attention.

  • Nicholas Westby 2054 posts 7100 karma points c-trib
    May 24, 2017 @ 21:12
    Nicholas Westby
    0

    Are you saying there is a property, sectionHeader, on your Guia do Usuário page which is an Archetype data type?

    If that's the case, you simply skip the code that you are "a little confused about". That is, you could do this:

    var menuData =
        // Current page.
        Model.Content
        // Find user's guide (must be an ancestor of the current page).
        .AncestorOrSelf("guiaDoUsuario")
        /* Skipped a step here, as it wasn't necessary in this case. */
        // Get menu (the Archetype property on the user guide content node).
        .GetPropertyValue<ArchetypeModel>("sectionHeader");
    

    Your loop would then enumerate the data you just got:

    foreach (var fieldset in menuData)
    {
        // Do stuff here.
    }
    

    Note that this will only work if this code is called from a page that is at or under the Guia do Usuário page (due to the use of AncestorOrSelf). Also, I'm not entirely sure what you're doing, as it confuses me that you've called your property sectionHeader. Maybe it makes sense to you, but I must be missing some context to understand it fully.

    what if the archetype property is stored on a page in the content tree and not in a separate document in a settings area?

    Just to be clear, the example I gave wasn't using the settings section of Umbraco. It was a normal content node in the content section of Umbraco. I wanted to make that clear, as I wasn't sure (based on the wording of your question) if that was apparent before.

  • Brett Fullam 119 posts 629 karma points
    May 24, 2017 @ 21:35
    Brett Fullam
    0

    Hi Nicholas,

    I'm sorry if the language I used was confusing ... this is all still very new to me.

    I did try removing that part of the statement, because I thought it wasn't necessary to access another node but it wasn't working. Tried it again with a slightly modified archetype name which is now "sectionHeaderData".

    Here's the archetype property on the doc type:

    enter image description here

    Here's the partial view code:

    @inherits Umbraco.Web.Mvc.UmbracoTemplatePage
    @using Archetype.Models;
    @using Archetype.Extensions;
    
    @{
        var menuData = Model.Content.AncestorOrSelf("guiaDoUsuario").GetPropertyValue<ArchetypeModel>("sectionHeaderData");
    
    
    foreach(var fieldset in menuData)
    {
        <div class="container-fluid heroWrap heroSpaceGuia">
            <div class="header-icon">   
    
    @if (Model.Content.HasValue("icon"))
    {
        var caseStudyImagesList1 = Model.Content.GetPropertyValue<string>("icon").Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse);
        var caseStudyImagesCollection1 = Umbraco.TypedMedia(caseStudyImagesList1).Where(x => x != null);
    
        foreach (var icon in caseStudyImagesCollection1)
            {      
                <img src="@icon.Url" />      
            }                                                               
    }
    
    
                </div>
            <div>
               <h1 class="internal-header">@Umbraco.Field("titulo")</h1>
            </div>
            <p class="intro-text">@Umbraco.Field("intro", removeParagraphTags: true)</p>
      </div>
    }
    } 
    

    I just realized something here that may be affecting this ... the page I'm trying to render the partial content on to is INHERITING the SAME archetype property type from the parent page (guia do usuario). Why do I get the feeling this is overriding the data from the archetype from the partent page?

  • Nicholas Westby 2054 posts 7100 karma points c-trib
    May 24, 2017 @ 21:58
    Nicholas Westby
    0

    Based on the screenshot of your document type, this code you have will not work:

    .AncestorOrSelf("guiaDoUsuario")
    

    It appears you are attempting to use the name of the page rather than the document type alias of the page. The document type alias appears to be guiaContentPage. So you'd do this instead:

    .AncestorOrSelf("guiaContentPage")
    

    the page I'm trying to render the partial content on to is INHERITING the SAME archetype property type from the parent page

    FYI, document types have changed a lot over years. I often see developers inheriting document types unnecessarily, seemingly attempting to mirror the content structure in the document type section. That's usually entirely unnecessary. Now that document type compositions exist, I don't think I inherit much at all if ever.

    Based on the code, this shouldn't really be causing you any problems though (aside from having extra unnecessary properties).

  • Brett Fullam 119 posts 629 karma points
    May 24, 2017 @ 22:07
    Brett Fullam
    0

    Thanks for your help and the extra insight. I'll go back and try to get this working with the correct page alias.

    The learning curve has been steep ... each time I learn something new I find myself going back and revising parts of the site (if the the entire site).

    I'm totally cool with taking a few steps backwards if I can push forward twice as far afterwards. I'm just used to having more documentation available to reference during the process. I can't even tell you how glad I am that the developers community here is so actively involved.

    Thanks again, Nicholas. I really appreciate your help.

  • Brett Fullam 119 posts 629 karma points
    May 25, 2017 @ 12:50
    Brett Fullam
    0

    Hi Nicholas,

    I actually managed to get it to work now ... not sure why ... changed platforms and now the code is good to go.

    Here's the final partial view code I used:

    @{
    var menuData = Model.Content.AncestorOrSelf("guiaDoUsuario").GetPropertyValue<ArchetypeModel>("hSectionItems");
    
    
    foreach(var fieldset in menuData)
    {
    
        <div class="container-fluid heroWrap heroSpaceGuia">
            <div class="header-icon">   
    
        @{
            var imageUrl = fieldset.GetValue<IPublishedContent>("hIcon").Url;
         }
         <img src="@imageUrl"/>  
    
    
                </div>
            <div>
               <h1 class="internal-header">@fieldset.GetValue("hTitle")</h1>
            </div>
           <div>@fieldset.GetValue("hIntro")</div>
      </div>
    
      }
      }
    

    Thanks again for your help working through this.

  • Nicholas Westby 2054 posts 7100 karma points c-trib
    May 15, 2017 @ 22:14
    Nicholas Westby
    1

    I personally tend to do things of this sort with Archetype: https://code101.net/links-in-umbraco-are-broken-but-alternatives-exist-1fe89aae2093

    You can build out hierarchical menus, and each link can include an optional suffix, which you can use to type in an anchor tag. Here's an example of a single link built with Archetype (from the article):

    Link in Archetype

    In the past, I have also created nested content nodes of a document type "Document Fragment", then created a custom URL provider that converts the last segment in the path into a document fragment (e.g., "/some/path" gets converted to "/some#path"). Not a huge fan of that approach, so I usually go with Archetype if I'm building a site from scratch.

  • Brett Fullam 119 posts 629 karma points
    May 16, 2017 @ 18:18
    Brett Fullam
    0

    Hi Nicholas,

    Thanks for the reference and the alternate solutions. If I were the only one managing the site I'd go this direction for sure, but I have to make the solution as simplified as I can for the client. They don't have a lot of experience and this will be complicated for them.

    I'm bookmarking Archetype for future use though. Thanks again!

  • Brett Fullam 119 posts 629 karma points
    May 23, 2017 @ 19:13
    Brett Fullam
    0

    Hey Nicholas ... I got my conditional statment to work ... now I'm trying to target that property type on the childpage. Check out the forum ... I posted the full-modified code.

    Any help would be appreciated.

  • Brett Fullam 119 posts 629 karma points
    May 22, 2017 @ 18:53
    Brett Fullam
    0

    OK ... so, I couldn't get archtype to work ... the psuedo code Nicholas shared with me (in just about every way I could think toimplement it) throws an error.

    I definitely want to go back an figure that one out. Archetype has solved a lot of challenges for me regarding page content.

    HOWEVER, in my frustration I went back to my original code and created a variation on the conditional statement (if, else if, else) that now looks for a simple true-false property type similar to umbracoNaviHide.

    Now I can effectively select-filter the document types that have the "related link" picker on them so they don't automatically inherit the url for that specific document-page.

    The only missing part of the puzzle for me now is how to target and include the link from the "related links picker".

    Here's the updated navigation partial view with the revised conditional statement.

    @inherits Umbraco.Web.Mvc.UmbracoTemplatePage
    @using Umbraco.Web.Models
    @*
        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 custom Razor helper called Traverse() to select and display the markup and links.
    *@
    
    @{ var selection = CurrentPage.Site(); 
    
        var isHome = true;
    
    }
    
    <nav class="nav navbar navbar-default" data-spy="affix" data-offset-top="197" >
    <div class="container navWrap col-centered">
    
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
        </div>
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
        <ul class="nav navbar-nav">
        @* Render the sitemap by passing the root node to the traverse helper, below *@
        @Traverse(selection, isHome)
        </ul>
            <form class="navbar-form navbar-right">
            <div class="form-group has-feedback">
              <input type="text" class="form-control" placeholder="Pesquisar"><i class="form-control-feedback glyphicon glyphicon-search"></i>
            </div>
            <!-- REMOVE BUTTON <button type="submit" class="btn btn-default">Enviar</button> -->
          </form>
        </div>
    </div>
    </nav>
    
    
    @* Helper method to travers through all descendants *@
    @helper Traverse(dynamic node, bool isHome)
    {
        @* Update the level to reflect how deep you want the sitemap to go *@
        var maxLevelForSitemap = 3;
    
        @* Select visible children *@
        var selection = node.Children.Where("Visible").Where("Level <= " + maxLevelForSitemap);
    
        @* If any items are returned, render a list *@
        if (selection.Any())
        {
    
    
                if(isHome) {
                                var cssClass = CurrentPage.Site().Id == CurrentPage.Id ? "active" : null;
                                isHome = false;
                            }
                foreach (var childPage in selection.Where("Visible"))
                            {   
    
                                if (childPage.Children.Any())
                                {                    
                                    <li class="has-child @(childPage.IsAncestorOrSelf(CurrentPage) ? "selected" : null)">
                                    <a class="menuHeader dropdown-toggle menuTextMain" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" href="#">@childPage.Name</a>
    
    
                                <ul class="dropdown-menu navItems">
                                    <li class="dropdown">
                                        @* Run the traverse helper again for any child pages *@
                                        @Traverse(childPage, isHome)
                                    </li>
                                </ul>
                                </li>
                                }
    
                                else if (childPage.GetPropertyValue("anchorLinkOption")==true)
                                {
                                    <li class="@(childPage.IsAncestorOrSelf(CurrentPage) ? "selected" : null)">
                                    <a class="menuTextMain" href="#">@childPage.Name</a>
                                    </li>
                                }
    
    
                                else
                                {
                                    <li class="@(childPage.IsAncestorOrSelf(CurrentPage) ? "selected" : null)">
                                    <a class="menuTextMain" href="@childPage.Url">@childPage.Name</a>
                                    </li>
                                }            
    
    
                            }
    
                }
    }
    

    I just can't seem to call the related links property to set the URL ... I was working on inserting this into the code but it keeps throwing this error "Value cannot be null. Parameter name: source" ... here's the modified block of code:

    else if (childPage.GetPropertyValue("anchorLinkOption")==true)
                                {
    
                                    var typedRelatedLinksConverted = Model.Content.GetPropertyValue<RelatedLinks>("anchorLinkadd");
    
                                    if (typedRelatedLinksConverted.Any())
                                    {
    
                                            foreach (var item in typedRelatedLinksConverted)
                                            {
                                                var linkTarget = (item.NewWindow) ? "_blank" : null;
    
                                                <li class="@(childPage.IsAncestorOrSelf(CurrentPage) ? "selected" : null)">
                                                    <a class="@item.Link" target="@linkTarget" href="#">@item.Caption</a>
                                                </li>
    
                                            }
    
                                    }
    
                                }
    

    I'm so close ... it's killing me that there's so little information online. The documentation for the related links data type is anorexic.

  • Brett Fullam 119 posts 629 karma points
    May 22, 2017 @ 20:13
    Brett Fullam
    0

    Here's a screen shot of the error ... enter image description here

  • Brett Fullam 119 posts 629 karma points
    May 23, 2017 @ 19:12
    Brett Fullam
    0

    Hey guys ... I got the related links issue RESOLVED (with the help of Jeavon Leopold ... I had installed Umbraco-Core-Property-Value-Converters package on top of what was already included in v7.6) and when the related links property type is on the SAME page that's loaded in the browser ... the code works PERFECTLY. The problem is when the property type is on another page ... then it'll throw an error for "System.ArgumentNullException: Value cannot be null" because the current page does not have this property type.

    The question I have now is ... how can I get the related links property value data from the childpage ...? I can totally get the code to see the the "anchorLinkOption" on the childpage to get it to look for the related links with this statement:

    else if (childPage.GetPropertyValue("anchorLinkOption")==true)
    

    but I'm having trouble modifying this line to get the related links:

    var typedRelatedLinksConverted = Model.Content.GetPropertyValue<RelatedLinks>("rLinksTarget");
    

    Here's the full code:

    @inherits Umbraco.Web.Mvc.UmbracoTemplatePage
    @using Umbraco.Web.Models
    @*
        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 custom Razor helper called Traverse() to select and display the markup and links.
    *@
    
    @{ var selection = CurrentPage.Site(); 
    
        var isHome = true;
    
    }
    
    <nav class="nav navbar navbar-default" data-spy="affix" data-offset-top="197" >
    <div class="container navWrap col-centered">
    
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
        </div>
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
        <ul class="nav navbar-nav">
        @* Render the sitemap by passing the root node to the traverse helper, below *@
        @Traverse(selection, isHome)
        </ul>
            <form class="navbar-form navbar-right">
            <div class="form-group has-feedback">
              <input type="text" class="form-control" placeholder="Pesquisar"><i class="form-control-feedback glyphicon glyphicon-search"></i>
            </div>
            <!-- REMOVE BUTTON <button type="submit" class="btn btn-default">Enviar</button> -->
          </form>
        </div>
    </div>
    </nav>
    
    
    @* Helper method to travers through all descendants *@
    @helper Traverse(dynamic node, bool isHome)
    {
        @* Update the level to reflect how deep you want the sitemap to go *@
        var maxLevelForSitemap = 3;
    
        @* Select visible children *@
        var selection = node.Children.Where("Visible").Where("Level <= " + maxLevelForSitemap);
    
        @* If any items are returned, render a list *@
        if (selection.Any())
        {
    
    
                if(isHome) {
                                var cssClass = CurrentPage.Site().Id == CurrentPage.Id ? "active" : null;
                                isHome = false;
                            }
                foreach (var childPage in selection.Where("Visible"))
                            {   
    
                                if (childPage.Children.Any())
                                {                    
                                    <li class="has-child @(childPage.IsAncestorOrSelf(CurrentPage) ? "selected" : null)">
                                    <a class="menuHeader dropdown-toggle menuTextMain" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" href="#">@childPage.Name</a>
    
    
                                <ul class="dropdown-menu navItems">
                                    <li class="dropdown">
                                        @* Run the traverse helper again for any child pages *@
                                        @Traverse(childPage, isHome)
                                    </li>
                                </ul>
                                </li>
                                }
    
                                else if (childPage.GetPropertyValue("anchorLinkOption")==true)
                                {
    
                                    var typedRelatedLinksConverted = Model.Content.GetPropertyValue<RelatedLinks>("rLinksTarget");
    
                                    if (typedRelatedLinksConverted.Any())
                                    {
    
                                            foreach (var item in typedRelatedLinksConverted)
                                            {
                                                var linkTarget = (item.NewWindow) ? "_blank" : null;
    
                                                <li class="@(childPage.IsAncestorOrSelf(CurrentPage) ? "selected" : null)">
                                                    <a class="@item.Link" target="@linkTarget" href="@item.Link">@item.Caption</a>
                                                </li>
    
                                            }
    
                                    }
    
                                }
    
    
                                else
                                {
                                    <li class="@(childPage.IsAncestorOrSelf(CurrentPage) ? "selected" : null)">
                                    <a class="menuTextMain" href="@childPage.Url">@childPage.Name</a>
                                    </li>
                                }            
    
    
                            }
    
                }
    }
    

    I'm SO CLOSE ...! Any ideas fellas?

  • Alex Skrypnyk 6131 posts 23950 karma points MVP 7x admin c-trib
    May 23, 2017 @ 21:43
    Alex Skrypnyk
    100

    Hi Brett

    Try this code, please:

            else if (childPage.GetPropertyValue("anchorLinkOption")==true)
            {
                if (childPage.HasValue("rLinksTarget") && childPage.rLinksTarget.ToString().Length > 2)
                {
                    foreach (var item in childPage.rLinksTarget)
                    {
                        var linkTarget = (item.NewWindow) ? "_blank" : null;
    
                        <li class="@(childPage.IsAncestorOrSelf(CurrentPage) ? "selected" : null)">
                            <a class="@item.Link" target="@linkTarget" href="@item.Link">@item.Caption</a>
                        </li>
                    }
                }
            }
    
  • Brett Fullam 119 posts 629 karma points
    May 24, 2017 @ 00:10
    Brett Fullam
    1

    SUCCESS ...! Wow, Alex ... Thank you for helping me complete this dynamic nav code. I'll post the revised-completed version for anyone else who'd like to use it (especially since it was collaborative effort to create it).

    HUGE help ... thank you again.

  • Brett Fullam 119 posts 629 karma points
    May 25, 2017 @ 13:31
    Brett Fullam
    1

    Here's the completed dropdown nav code block for anyone else that might be interested:

    @inherits Umbraco.Web.Mvc.UmbracoTemplatePage
    @using Umbraco.Web.Models
    @*
        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 custom Razor helper called Traverse() to select and display the markup and links.
    *@
    
    @{ var selection = CurrentPage.Site(); 
    
        var isHome = true;
    
    }
    
    <nav class="nav navbar navbar-default" data-spy="affix" data-offset-top="197" >
    <div class="container navWrap col-centered">
    
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
        </div>
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
        <ul class="nav navbar-nav">
        @* Render the sitemap by passing the root node to the traverse helper, below *@
        @Traverse(selection, isHome)
        </ul>
            <form class="navbar-form navbar-right">
            <div class="form-group has-feedback">
              <input type="text" class="form-control" placeholder="Pesquisar"><i class="form-control-feedback glyphicon glyphicon-search"></i>
            </div>
            <!-- REMOVE BUTTON <button type="submit" class="btn btn-default">Enviar</button> -->
          </form>
        </div>
    </div>
    </nav>
    
    
    @* Helper method to travers through all descendants *@
    @helper Traverse(dynamic node, bool isHome)
    {
        @* Update the level to reflect how deep you want the sitemap to go *@
        var maxLevelForSitemap = 3;
    
        @* Select visible children *@
        var selection = node.Children.Where("Visible").Where("Level <= " + maxLevelForSitemap);
    
        @* If any items are returned, render a list *@
        if (selection.Any())
        {
    
    
                if(isHome) {
                                var cssClass = CurrentPage.Site().Id == CurrentPage.Id ? "active" : null;
                                isHome = false;
                            }
                foreach (var childPage in selection.Where("Visible"))
                            {   
    
                                if (childPage.Children.Any())
                                {                    
                                    <li class="has-child @(childPage.IsAncestorOrSelf(CurrentPage) ? "selected" : null)">
                                    <a class="menuHeader dropdown-toggle menuTextMain" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" href="#">@childPage.Name</a>
    
    
                                <ul class="dropdown-menu navItems">
                                    <li class="dropdown">
                                        @* Run the traverse helper again for any child pages *@
                                        @Traverse(childPage, isHome)
                                    </li>
                                </ul>
                                </li>
                                }
    
                                  else if (childPage.GetPropertyValue("anchorLinkOption")==true)
            {
                if (childPage.HasValue("rLinksTarget") && childPage.rLinksTarget.ToString().Length > 2)
                {
                    foreach (var item in childPage.rLinksTarget)
                    {
                        var linkTarget = (item.NewWindow) ? "_blank" : null;
    
                        <li class="@(childPage.IsAncestorOrSelf(CurrentPage) ? "selected" : null)">
                            <a class="menuTextMain" target="@linkTarget" href="@item.Link">@item.Caption</a>
                        </li>
                    }
                }
            }
    
    
    
                                else
                                {
                                    <li class="@(childPage.IsAncestorOrSelf(CurrentPage) ? "selected" : null)">
                                    <a class="menuTextMain" href="@childPage.Url">@childPage.Name</a>
                                    </li>
                                }            
    
    
                            }
    
                }
    }
    
Please Sign in or register to post replies

Write your reply to:

Draft