Copied to clipboard

Flag this post as spam?

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


  • Andy Cox 31 posts 182 karma points
    Nov 02, 2017 @ 16:52
    Andy Cox
    0

    Getting the URL of a page.

    Hi All,

    I've been scratching my head for a while about this but I can seem to find the answer I'm looking for! Sorry if the question is stupid!

    How can I get get the URL of a different page? I've got a product page which I have a button to link to the 'Find a Stockist' page but I can't figure out a good way to get the URL.

    The 'Find a Stockist' page does not have a custom controller so I'm a little stumped! I could use @Umbraco.Url(contentId) but this means I'd have to hard code the content id into the view! Is there any other way of doing this?

    Sorry, it's been a long day and I'm sure I'm missing something!

    Thanks,

    Andy

  • Nicholas Westby 2054 posts 7100 karma points c-trib
    Nov 02, 2017 @ 17:27
    Nicholas Westby
    4

    I could use @Umbraco.Url(contentId) but this means I'd have to hard code the content id into the view!

    I wouldn't say you need to hard code the content ID. It just means you need to obtain the content ID somehow.

    I usually do this by creating a content picker property on the homepage node, which allows me to pick the page I'm interested in. I can then obtain the homepage (it's the top-most ancestor of any page), then get the value of that picked node from the property.

    There are many ways of going about this, but I would guess that one will suffice for your situation. Note that I'm assuming you have exactly one "Find a Stockist" page on your site.

  • Andy Cox 31 posts 182 karma points
    Nov 03, 2017 @ 08:42
    Andy Cox
    0

    Hi Nicholas,

    Thanks for the reply.

    I could add the content picker, as you suggest, to a globally accessible node, such as a Global Settings node, which I would prefer rather than on the homepage but what about other pages? Would I need to add content pickers for all the pages that I might need to reference throughout the site? I can image this becoming very cumbersome.

    Alternatively, I could add a content picker to the product page to enable setting the 'Stockist' link but I would have to populate this for every product that I add. With a few hundred products this can become problematic.

    I guess what I am looking for is a UrlHelper method that could be used to find content in a friendlier way but I'm not sure if there is anything available out of the box!

    Any advise welcome!

    Thanks again for your help. :)

    Andy

  • Michaël Vanbrabandt 863 posts 3348 karma points c-trib
    Nov 03, 2017 @ 09:05
    Michaël Vanbrabandt
    1

    Hi Andy,

    if its only one page that has a unique document type, then you can do something like the following.

    In my projects I create an extension class which contains extension methods to the UmbracoHelper.

    Like for example my contact page, there if only one content node of it and its of doc type contact.

    So in my extension class I do:

        public static IPublishedContent GetContact(this UmbracoHelper helper)
        {
            return helper.TypedContentSingleAtXPath("//home/contact");
        }
    

    What this does is using XPATH to find the first node of doc type contact under the home node.

    Then in my views I can do:

    var contact = Umbraco.GetContact();
    

    *Make sure to add the namespace of the extension class to your views.

    Then for getting the url you can do:

    <a href="@contact.Url">Contact</a>
    

    Hope this helps.

    /Michaël

  • Andy Cox 31 posts 182 karma points
    Nov 03, 2017 @ 10:00
    Andy Cox
    100

    Thanks for that Michael. I've gone with something slightly different based off your idea.

        /// <summary>
        /// Gets the url of a content node identified by its document type alias.
        /// </summary>
        /// <param name="umbracoHelper">The Umbraco helper.</param>
        /// <param name="documentTypeAlias">The document type alias.</param>
        /// <returns>The url for the content.</returns>
        public static string Url(this UmbracoHelper umbracoHelper, string documentTypeAlias)
        {
            var content = umbracoHelper.TypedContentSingleAtXPath($"//{documentTypeAlias}");
    
            return umbracoHelper.Url(content.Id);
        }
    

    This way the content is also passed through any registered URL providers. I've then registered my UmbracoHelperExtensions class in the views web.config:

    <pages pageBaseType="System.Web.Mvc.WebViewPage">
      <namespaces>
        ...
        <add namespace="umbraco" />
        <add namespace="Examine" />
        <add namespace="Umbraco.Web.PublishedContentModels" />
        <add namespace="Path.To.My.Extensions" />
      </namespaces>
    

    Then in my Views I can use the extension:

    <a href="@Umbraco.Url(StockistPage.ModelTypeAlias)" class="btn btn-primary">Find a stockist</a>
    

    This is probably not the best solution but I'll go with it for now. I'll also need to think how to handle parameters and content with multiple nodes.

    Thanks again,

    Andy

  • David Parr 48 posts 206 karma points
    Nov 03, 2017 @ 10:41
    David Parr
    0

    If you have a template-less content type of settings and then have a single settings content node at the root of a site, you can write an extension method that can find the settings content.

    Here is some example code for you:

    using System.Linq;
    using Umbraco.Core.Models;
    using Umbraco.Web;
    
    namespace MySite.Web.Extensions
    {
        public static class PublishedContentExtensions
        {
            /// <summary>
            /// Find the settings content for a site. This approach allows all sites in a multi site setup to have their own individual settings node,
            /// and also allows for multiple setting nodes within a site, for example if you have different sections.
            /// </summary>
            /// <param name="current"></param>
            /// <param name="contentTypeAlias"></param>
            /// <returns></returns>
            public static IPublishedContent GetSiteSettings(this IPublishedContent current, string ancestorContentTypeAlias = "home", string settingsContentTypeAlias = "settings")
            {
                IPublishedContent settings = null;
                var ancestor = current.AncestorOrSelf(ancestorContentTypeAlias);
    
                if(ancestor != null)
                {
                    settings = ancestor.Children.SingleOrDefault(c => c.DocumentTypeAlias == settingsContentTypeAlias);
                }
    
                return settings;
            }
        }
    }
    

    Then in your views or wherever an instance of IPublishedContent is being consumed you can use any setting you have, for a content picker for example, in a razor view:

    @{
        var settings = @Model.Content.GetSiteSettings();
        var stockistContent = Umbraco.TypedContent(settings.GetPropertyValue<int>("stockistContentPicker"));
    }
    
    @stockistContent.Url;
    

    Thanks

    P.S The above code is untested, I just wrote this here top of my head. You may want to refactor, you can also add a generic type argument incase you're using strongly typed models. So you could have the following as well:

    GetSiteSettings<T>...
    
  • Andy Cox 31 posts 182 karma points
    Nov 03, 2017 @ 11:01
    Andy Cox
    0

    Thanks David.

    My Global Settings node is actually setup in the DI container and injected into my PageModel so I already have access to it in the views. I just don't like the idea of having to add content pickers, to the global settings, for all of the links that I may want to access throughout the site.

    Thanks again.

    Andy

  • David Parr 48 posts 206 karma points
    Nov 03, 2017 @ 11:16
    David Parr
    0

    Am I right in thinking you have just one 'stockist' page type? If so I'm confused as to why you need to set this up in multiple content pickers as you described above?

    If not, then maybe I've not drank enough coffee yet this morning. :)

  • Andy Cox 31 posts 182 karma points
    Nov 03, 2017 @ 11:25
    Andy Cox
    0

    Hi David,

    Yes, there is only one 'Stockists' page but what if I need the URL to the 'Contact' page or the 'About' page? Do I need to add a content picker for those pages too?

    As my site grows do I need to add content pickers for all the other pages that I might want to reach? That seems like a poor solution to me.

    Thanks,

    Andy

  • David Parr 48 posts 206 karma points
    Nov 03, 2017 @ 13:40
    David Parr
    0

    Not sure I'm following you. Where do you need to use these other url's? For the use in nav menus? If so then why not just iterate from the home site downwards and build your menus based on umbracoNaviHide or a custom property in your Razor views?

    Something like this:

    @inherits Umbraco.Web.Macros.PartialViewMacroPage
    
    
    @*
        Macro to display child pages below the root page of a standard website.
        Also highlights the current active page/section in the navigation with
        the css class "current".
    *@
    
    @{
        @* Get the root of the website *@
        var root = CurrentPage.AncestorOrSelf(1);
    }
    
    <ul>
        @foreach (var page in root.Children.Where("Visible"))
        {
            <li class="@(page.IsAncestorOrSelf(CurrentPage) ? "current" : null)">
                <a href="@page.Url">@page.Name</a>
            </li>
        }
    </ul>
    

    You should only use a content picker for providing content editors a way to set links to pages, for use with content (say a media gallery, list or such).

  • Andy Cox 31 posts 182 karma points
    Nov 03, 2017 @ 13:56
    Andy Cox
    0

    I think perhaps we're getting our wires crossed. :)

    I have navigation elements that use something similiar to what you've suggested for the top navigation, main navigation, breadcrumbs, quick links etc... These are all parsed, cached and sent to the view in ViewModels. But all I was trying to do was generate the URL to a page without having to explicitly pick that page or rely on walking the content tree to find it.

    We have links across our site that point to different pages and I was just after something of equivalent or similar functionality to the Url.Action method on the UrlHelper.

    I think for our current needs the solution that I've present above will be sufficient.

  • David Parr 48 posts 206 karma points
    Nov 03, 2017 @ 16:52
    David Parr
    2

    I'm with you now. :)

    The solution you posted above looks good, and that combined with content pickers is how most of us do it 99% of the time I would think. Just be aware of the following:

    • It only works if there is only one content node of a given type/alias.
    • You will run into problems if you have multiple root nodes, say in a multi site setup, in which case you'd want to be more specific in the xpath query.
    • This relies on a context that has Umbraco helper readily available. If you wanted to ever fetch URL's elsewhere you'd need to make sure to have an instance of UmbracoHelper.

    Even given the above, I can't think of any other solution that is as straightforward to implement as this one. Anything else, even if it solved the above rarities, would be overkill I think, since in my opinion standalone querying of any content's URL should be a rarity.

    Thanks

Please Sign in or register to post replies

Write your reply to:

Draft