DEV Community

Chris Cooper
Chris Cooper

Posted on • Edited on

Breadcrumbs within Razor page .Net Core

I'm still quite new to Razor pages (not mvc) and wanted a Breadcrumb navigation bar on my pages, which was created dynamically. I looked around but in the end I created my own and this is how I did it. There is a full example of my solution on a .net core razor page solution on git.

The Breadcrumb creation

This is created within a class that will be inherited on the pagemodel class. This is broken down into many smaller part to make it easier.

Validating the Request

Firs you need to make sure the Request has a path and a query string, before you start looking at making the bread crumbs

    public void Validate()
    {
        currentQuery = String.Empty;
        currentPath = String.Empty;
        referers = this.Request.Headers["Referer"].ToList(); ;
        if (this.Request.Path.HasValue)
            currentPath = this.Request.Path.Value.Replace("/", String.Empty);
        if (this.Request.QueryString.HasValue)
            currentQuery = this.Request.QueryString.Value;

    }

Get current bread crumbs

This logic uses a session with a JSON object, containing all needed data for my breadcrumbs

    private  void getCurrentBreadCrumbs()
    {
        var sessionString = HttpContext.Session.GetString("BreadCrumbs");
        breadCrumbs = new List<DataClassesLibrary.BreadCrumbDataClass>();
        if (String.IsNullOrWhiteSpace(sessionString) || sessionString.Equals("[]"))
            return;

        breadCrumbs = (List<DataClassesLibrary.BreadCrumbDataClass>)HelperLibrary.JsonStringHelper.GetObject(sessionString, breadCrumbs.GetType());

    }

Setting new bread crumb

Now I have my current and a valid request object, I can see if I need to set a new bread crumb for the current page.

    private  void setBreadCrumbs()
    {
        if (referers.Count > 0)
        {
            var urlValue = referers[0].ToString();
            var urlElements = urlValue.Split("/").ToList();
            var hostLocation = urlElements.IndexOf(this.Request.Host.Value);
            if (urlElements.Count() > hostLocation)
            {
                var razorPageURL = urlElements[hostLocation + 1];
                var razorPage = razorPageURL.Split("?");
                if (razorPage.Count() > 0)
                {
                    var data = new DataClassesLibrary.BreadCrumbDataClass();
                    data.Key = razorPage[0].ToString();
                    //Make sure I'm not adding the page back in I have removed
                    if (crumbRemoved.FirstOrDefault(x => x.Equals(data.Key)) != null)
                        return;

                    if (setCurrentAsDisabled(data.Key))                        
                        return;//Do not set if already exists


                    if (razorPage.Count().Equals(2))
                    {                            
                        data.Parameters = setParameters(razorPage[1]);
                    }
                    breadCrumbs.Add(data);
                }

            }                
        }
    }

Set the session

Because I use a session with a JSON object, to hold my current breadcrumbs, I will set the session with the new data.

    private void setSession()
    {
        HttpContext.Session.Set("BreadCrumbs", System.Text.Encoding.UTF8.GetBytes(HelperLibrary.JsonStringHelper.GetString(breadCrumbs)));
    }

Creating the HTML breadscrumb

This goes through my Bread crumbs List object and creates the required simple HTML.

Navigation bar

This is the main method that creates the navigation bar

    public string SetNavString(List<DataClassesLibrary.BreadCrumbDataClass> crumbs)
    {
        if (crumbs == null || crumbs.Count.Equals(0))
            return String.Empty;

        var nav = new StringBuilder();
        nav.AppendLine("<nav aria-label=\"breadcrumb\">");
        nav.AppendLine("<ol class=\"breadcrumb\">");

        foreach (var crumb in crumbs)
            nav.AppendLine(setLinkItem(crumb));

        nav.AppendLine("</ol>");
        nav.AppendLine("</nav>");
        return nav.ToString();
    }

Creating the linked item

This is how I have created the linked item, within the Navigation bar

    private static string setLinkItem(DataClassesLibrary.BreadCrumbDataClass crumb)
    {
        var item = new StringBuilder();
        if (!crumb.Current)
        {
           item.AppendLine("<li class=\"breadcrumb-item \">");
           item.AppendLine(String.Format("<a href=\"{0}{1}\" >{0}</a>", crumb.Key, setLinkItemParemeters(crumb)));
        }
        else
        {
            item.AppendLine("<li class=\"breadcrumb-item active\" aria-current=\"page\">");
            item.AppendLine(crumb.Key);
        }

        item.AppendLine("</li>");
        return item.ToString();
    }

Setting parameters

Setting the parameters for a linked item

    private static string setLinkItemParemeters(DataClassesLibrary.BreadCrumbDataClass crumb)
    {
        if (crumb.Parameters == null || crumb.Parameters.Count.Equals(0))
            return String.Empty;

        var parameter = new List<string>();
        foreach (var p in crumb.Parameters)
        {
            parameter.Add(String.Format("{0}={1}", p.Key, p.Value));
        }

        return "?" + String.Join("&", parameter.ToArray());
    }

Invoke the build

Once you have inherited the build class you call the build or reset method on the OnGet method.

public class FlagListModel : Pages.Extended.BreadCrumbsExtended
{

    public void OnGet()
    {
        SetBreadCrumbs();          

    }
}

Bread crumbs extended class

The SetBreadCrumbs method sits within the extended class and looks like below

    public  void SetBreadCrumbs()
    {   
        currentQuery = String.Empty;
        currentPath = String.Empty;
        referers = this.Request.Headers["Referer"].ToList(); ;
        if (this.Request.Path.HasValue)
            currentPath = this.Request.Path.Value.Replace("/", String.Empty);
        if (this.Request.QueryString.HasValue)
            currentQuery = this.Request.QueryString.Value;

        getCurrentBreadCrumbs();

        checkGoingBack();
        setBreadCrumbs();

        setCurrent();
        setSession();

        BreadCrumb = BreadCrumbsNavigationExtended.SetNavString(breadCrumbs);
        //Dispos
        currentQuery = String.Empty;
        currentPath = String.Empty;
        referers = null;
        breadCrumbs = null;
    }

Show the navigation bar
Now you have inherited and invoked the build, you want to show the navigation bar. This is done simply by placing the single line.

@Html.Raw(Model.BreadCrumb)

Now you have inherited and invoked the build, you want to show the navigation bar. This is done simply by placing the single line.

Full solution
The full solution can be found within my public GitHub repo.
The SetBreadCrumbs logic is done on the Privacy page. The WipeBreadCrumbs logic is done on the index (home) page

GitHub

Top comments (11)

Collapse
 
bunda3d profile image
Vom Com

Hi Chris,

Can you explain how to add the breadcrumbs directive to the shared "_Layout" file so it can show up on all pages that use that layout?

I downloaded your example from the github repo, and when I tried to move the breadcrumbs nav directive from "privacy" to the layout view, it doesn't work...I'm getting errors. Can you think of a solution?

Thanks!

Collapse
 
chriscooper01 profile image
Chris Cooper

Can you please share the error?
You would need to inherit and load the breadcrumbs on each pagemodal, that used the layout you have added the Breadcrumb.

Collapse
 
bunda3d profile image
Vom Com • Edited

Here's a screenshot of the browser stack trace.

(Edited because it doesn't seem to be showing the img attachment)

dev-to-uploads.s3.amazonaws.com/i/...

It's after I moved the declaration to the layout page and added a new razor page named "test1", navigating to it from the index page is the action that triggers this error.

Thread Thread
 
chriscooper01 profile image
Chris Cooper

So its when you acces the new page. On the pagemodel for test1, have you inherited the BreadCrumbsExtended class? Then on the get for the page have you called the SetBreadCrumbs() method?

This will be needed on each page that uses this layout.

Thread Thread
 
bunda3d profile image
Vom Com

Chris, thanks for the prompt replies. I just got pulled into something else, I'll try to get back to this soon and let you know if that was the problem (sounds right... I saw the Get action on the Privacy page 'code behind' sheet, but can I put an action like that on a _Layout view?) I think I was extending the correct class, but I'm not sure I know how to implement the functionality on a _Layout page. Maybe it would make more sense to do on a _Partial view or Component? And then I could more easily execute code in there, while calling the partial or component (which contains the breadcrumb menu line) from the _Layout view, which could access the current active page/view being routed to/viewed?

I'm just trying to get a breadcrumb nav to work without hard-coding every page. I've tried several examples, but one feature or another seems to fail in my solution (has a shared "Presentation Layer" project that compiles to a nuget pkg so other apps can install it and have a consistent front-end and stay DRY). This seems to make it difficult to support the circumstances of loading and hosting other assemblies in .NET Core, some with razor pages/routing and/or controllers/views. Getting more "dynamic" breadcrumb navigation to work as a silver bullet for those use cases has been confusing... Any tips on what to try?

Thread Thread
 
chriscooper01 profile image
Chris Cooper

Hi
Don't think you can run the action on the layout, it needs to be on the code-behind for each page. But if you do try and it works let us now, it would be good to know.

You could go with the partial view, but it would need a partial view with a code behind.

When you do get back to it, let me know your end result, solution.

Thanks, hope this solution helps you

Thread Thread
 
chriscooper01 profile image
Chris Cooper

With tips, I did not look at nugets or third part codes. I just went ahead on doing my own. So now able to give any tips sorry.

But if you have the partial view working, it should be dynamic for you. You might want to alter the code to improve the display name.

Collapse
 
tommy_klausen_0842480edd2 profile image
Tommy Klausen

Hi Chris! Do you know how I can translate/convert/map the class names to something more meaningfull og readable for the user? My class names arent really understandable for which page were talking about in the breadcrumb.

Collapse
 
debadattaratha profile image
DebadattaRatha • Edited

Chris, Can you paste a link to this solution on a .net core razor page solution on git here ?

Collapse
 
chriscooper01 profile image
Chris Cooper

Hi
I've published it to GitHub
github.com/chriscooper01/BreadCrumbs

Collapse
 
chriscooper01 profile image
Chris Cooper • Edited

Hi
I will publish this to my github tomorrow as it does not seem to be there. Once done, I will add the repo url here.