Search code examples
mvcsitemapproviderasp.net-mvc-sitemap

How to use Html.MvcSiteMap().Menu() with dynamic parameter?


I am using MvcSiteMapProvider 4.6.3, MVC 4.

I want to use Html.MvcSiteMap().Menu() to generate the menu.

The problem is I have multiple parameters in the URLs, which is changed based on user and document id.

My Sitemap looks like:

<mvcSiteMapNode title="Home" controller="Home" action="Index">
  <mvcSiteMapNode title="Site Map Test" controller="SitemapTest" action="Index" area="" key="sitemaptestnode">
    <mvcSiteMapNode title="Sub1" controller="SitemapTest" action="Sub1" area="" />
    <mvcSiteMapNode title="Sub2" controller="SitemapTest" action="Sub2" area="" />
  </mvcSiteMapNode>
</mvcSiteMapNode>

These actions need different parameters.

    public ActionResult Index(string id, string clientId)
    {
        return View();
    }

    public ActionResult Sub1(string id, string productId)
    {
        return View();
    }

    public ActionResult Sub2(string id, string staffId)
    {
        return View();
    }

I try to use the Html helper to generate the Menu.

@Html.MvcSiteMap().Menu(node, false, false)

So if I am on the Index page. And the Menu should be

SitemapTest/Index/product1?clientId=clientId2

SitemapTest/Index/client1?productId=product2

SitemapTest/Index/client1?staffId=staffId1

These ids are getting from database.

Is there a way to pass the parameter to the Html help to specify which Id to use for each link?


Solution

  • There are 2 ways, and it depends on whether the route values are directly used to identify page, or are some ambient value.

    For values that directly identify the page, you should configure the nodes using IDynamicNodeProvider or ISiteMapNodeProvider so they can build new nodes when new records are added to the database.

    public class ProductDynamicNodeProvider 
        : DynamicNodeProviderBase 
    { 
        public override IEnumerable<DynamicNode> GetDynamicNodeCollection(ISiteMapNode node) 
        {
            // TODO: Replace MyEntities with your entity framework context object
            using (var db = new MyEntities())
            {
                // Create a node for each album 
                foreach (var album in db.Products) 
                { 
                    DynamicNode dynamicNode = new DynamicNode(); 
                    dynamicNode.Title = product.Name; 
                    dynamicNode.ParentKey = "Products"; // There must be a node in the SiteMap with key set to "Products" for this to work
    
                    dynamicNode.PreservedRouteParameters.Add("id"); // Force a match on this parameter always
                    dynamicNode.RouteValues.Add("productId", product.Id);
    
                    yield return dynamicNode;
                }
            }
        } 
    }
    

    And for dynamic node providers, you need a definition node that will be used as a template when creating the nodes, and it will not actually exist in the SiteMap.

    // This node will be added to the SiteMap and serves as the parent node of each product
    <mvcSiteMapNode title="All Products" controller="Products" action="Index" area="" key="Products">
        // This node will become a template for each product node, but the node itself won't be added to the SiteMap
        <mvcSiteMapNode controller="Products" action="Details" area="" dynamicNodeProvider="MyNamespace.ProductDynamicNodeProvider, MyAssemblyName" />
    </mvcSiteMapNode>
    

    For ambient values (userId, sessionId, etc.), you can force the SiteMap to always match them by configuring them as preservedRouteParameters.

    <mvcSiteMapNode title="Sub2" controller="SitemapTest" action="Sub2" area="" preservedRouteParameters="clientId,productId,staffId,id" />
    

    You can also combine these 2 techniques by forcing a match on certain parameters and providing node instances for combinations of other parameters.

    For an in depth look at these options, read How to Make MvcSiteMapProvider Remember a User's Position.