-
Notifications
You must be signed in to change notification settings - Fork 216
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add HTML helpers for Previous() and Next() nodes #363
Comments
The SiteMap does not keep track individual user's movements. It only builds a set of links based on the user's current position within the map. This functionality is not build into the API because there would be no practical way of determining this without some kind of session tracking. I have written an article titled How to Make MvcSiteMapProvider Remember a User's Position to explain why this is not possible with this design. Here is the introduction from that post:
|
Just to make sure we are talking about the same thing: as there is |
Ok, but it is still a bit unclear what you are trying to achieve. The current node corresponds to the current HTTP request. But what should determine a "next node" and a "previous node"? If you put your nodes into an Mvc.sitemap XML format and post them here and tell me which one is the "current node" and what node you are expecting to get to as the "next node" and "previous node" as well as what HTML output you are expecting from such an HTML helper, I might be able to provide you some direction. For example, if you were creating a wizard, you would probably want to model your nodes in such a way that they build a breadcrumb trail that shows all of the previous steps. Start > User Info > Company Info > Clients > Survey > Finish And if you were currently on the "Comany Info" step, your breadcrumb would look like this: Start > User Info > Company Info In this case, you would probably want to make a named SiteMapPath that is only used for your wizard, so the nodes don't show up on any of your regular navigation.
You would model such a wizard like this in XML: <mvcSiteMapNode title="Home" controller="Home" action="Index" visibility="!SignupWizard">
<mvcSiteMapNode title="Sign Up" controller="SignupWizard" action="Index" visibility="!SignupWizard">
<mvcSiteMapNode title="Start" action="Start">
<mvcSiteMapNode title="User Info" action="UserInfo">
<mvcSiteMapNode title="Company Info" action="CompanyInfo">
<mvcSiteMapNode title="Clients" action="Clients">
<mvcSiteMapNode title="Survey" action="Survey">
<mvcSiteMapNode title="Finish" action="Finish"/>
</mvcSiteMapNode>
</mvcSiteMapNode>
</mvcSiteMapNode>
</mvcSiteMapNode>
</mvcSiteMapNode>
</mvcSiteMapNode>
</mvcSiteMapNode> So your "previous node" would be the parent node of the current node. The "next node" would be the first child of the next level in this case, but what if you wanted to branch your wizard at a certain step depending on what the user selects? <mvcSiteMapNode title="Home" controller="Home" action="Index" visibility="!SignupWizard">
<mvcSiteMapNode title="Sign Up" controller="SignupWizard" action="Index" visibility="!SignupWizard">
<mvcSiteMapNode title="Start" action="Start">
<mvcSiteMapNode title="User Info" action="UserInfo">
<mvcSiteMapNode title="Company Info" action="CompanyInfo">
<mvcSiteMapNode title="Clients" action="CompanyClients">
<mvcSiteMapNode title="Survey" action="CompanySurvey">
<mvcSiteMapNode title="Finish" action="CompanyFinish"/>
</mvcSiteMapNode>
</mvcSiteMapNode>
</mvcSiteMapNode>
<mvcSiteMapNode title="Personal Info" action="PersonalInfo">
<mvcSiteMapNode title="Survey" action="PersonalSurvey">
<mvcSiteMapNode title="Finish" action="PersonalFinish"/>
</mvcSiteMapNode>
</mvcSiteMapNode>
</mvcSiteMapNode>
</mvcSiteMapNode>
</mvcSiteMapNode>
</mvcSiteMapNode> Now the user can either select to signup for their company or for their personal use. The "next node" at the "User Info" step would be conditional depending on what the user's input is. Therefore, logic that determines the "next node" would be completely dependent on the design of the wizard. |
I have a docs site with a structure like this:
When I am on the So it's not a wizard, it's a navigation in a hierarchical structure that despite being hierarchical always has a well-defined "previous" and "next" nodes. |
I have created a demo project showing how you can extend the functionality of MvcSiteMapProvider by making your own HTML helpers. For the sake of consistency, I made them all templated HTML helpers (just like the built-in HTML helpers) but there is no reason why you couldn't make HTML helpers that output the HTML elements you want as a string instead of referring to a template. The real magic happens in the GetNextNode and GetPreviousNode methods: private static ISiteMapNode GetNextNode(ISiteMapNode startingNode, IDictionary<string, object> sourceMetadata)
{
ISiteMapNode nextNode = null;
if (startingNode.HasChildNodes)
{
// Get the first child node
nextNode = startingNode.ChildNodes[0];
}
else if (startingNode.ParentNode != null)
{
// Get the next sibling node
nextNode = startingNode.NextSibling;
if (nextNode == null)
{
// If there are no more siblings, the next position
// should be the parent's next sibling
var parent = startingNode.ParentNode;
if (parent != null)
{
nextNode = parent.NextSibling;
}
}
}
// If the node is not visible or accessible, run the operation recursively until a visible node is found
if (nextNode != null && !(nextNode.IsVisible(sourceMetadata) || nextNode.IsAccessibleToUser()))
{
nextNode = GetNextNode(nextNode, sourceMetadata);
}
return nextNode;
}
private static ISiteMapNode GetPreviousNode(ISiteMapNode startingNode, IDictionary<string, object> sourceMetadata)
{
ISiteMapNode previousNode = null;
// Get the previous sibling
var previousSibling = startingNode.PreviousSibling;
if (previousSibling != null)
{
// If there are any children, go to the last descendant
if (previousSibling.HasChildNodes)
{
previousNode = previousSibling.Descendants.Last();
}
else
{
// If there are no children, return the sibling.
previousNode = previousSibling;
}
}
else
{
// If there are no more siblings before this one, go to the parent node
previousNode = startingNode.ParentNode;
}
// If the node is not visible or accessible, run the operation recursively until a visible node is found
if (previousNode != null && !(previousNode.IsVisible(sourceMetadata) || previousNode.IsAccessibleToUser()))
{
previousNode = GetPreviousNode(previousNode, sourceMetadata);
}
return previousNode;
} The rest of the code is mostly just boilerplate stuff to make the HTML helper work and to provide ways to override the text that is displayed, template used, node that is considered the "current node", etc. I think this would make a good thing to add to MvcSiteMapProvider as a feature, but I need to consider how to make it clear that this is not the same thing as the forward and back buttons of the browser (which is what people normally expect). Maybe I could call them |
This is seriously awesome, thanks for the example! As for the naming, in my mind, I want a "next / previous node in a site map" so methods called |
Would be nice if there was an easy way to generate links to next and previous nodes in HTML, using something like:
I had a quick look at the API but didn't find anything that would expose this directly.
The text was updated successfully, but these errors were encountered: