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
Top comments (11)
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!
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.
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.
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.
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?
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
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.
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 aren
t really understandable for which page we
re talking about in the breadcrumb.Chris, Can you paste a link to this solution on a .net core razor page solution on git here ?
Hi
I've published it to GitHub
github.com/chriscooper01/BreadCrumbs
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.