-
Notifications
You must be signed in to change notification settings - Fork 218
Using Custom Attributes on a Node
MvcSiteMapProvider supports adding user-defined data to the nodes. This is useful if you need to make customizations that require extra values to be configured for an individual node.
By default, when you add a custom attribute, it is automatically added to both the Attributes and RouteValues dictionaries of the node. This is because for MVC the primary usage of a custom attribute is to add a custom route value. When values are added to RouteValues, they also automatically show up in the URL that is generated.
For example, if you were to add "id" and "type" attributes to your node, the result would be that you would have "id" and "type" attributes in both the Attributes and RouteValues properties of the ISiteMapNode instance.
<mvcSiteMapNode title="Home" controller="Home" action="Index">
<mvcSiteMapNode title="Flashlight with Remote Control" controller="Product" action="Details" id="45" type="camping-supplies"/>
</mvcSiteMapNode>
The "Flashlight with Remote Control" node would therefore have a URL that is resolved with both "id" and "type" parameters in it. Exactly how the URL gets resolved is controlled by your ASP.NET route configuration. You can change the ASP.NET route to get a different result.
If you use the above example with the default MVC route, you will get a URL generated as "/Product/Details/45?type=camping-supplies".
You can specify to exclude adding the attribute to the RouteValues dictionary so it doesn't affect the generated URL. This allows you to specify attributes that can be used for other purposes, such as providing additional data for use in an HTML helper template.
To exclude the attribute "type" in our example above example, you can add the name of the attribute to the "MvcSiteMapProvider_AttributesToIgnore" appSettings value in the web.config file.
<appSettings>
<add key="MvcSiteMapProvider_AttributesToIgnore" value="type" />
</appSettings>
If using an external DI container, this setting can be found on the constructor of the ReservedAttributeNameProvider in a parameter named "attributesToIgnore", which is type IEnumerable<string>
.
Now when the URL is generated, it will no longer include the "type" attribute: "/Product/Details/45".
With MvcSiteMapNodeAttribute, adding custom attributes works exactly the same way. However, due to data type restrictions of .NET attributes, the MvcSiteMapNodeAttribute.Attributes property must be declared as a JSON string.
[MvcSiteMapNode(Title = "Flashlight with Remote Control", Attributes = @"{ ""id"": ""45"", ""type"": ""camping-supplies"" }")]
Whether or not the "type" parameter is added to the "AttributesToIgnore" setting, it is made available in the Attributes property of the node for use in your views and controller actions. For example, we can use the "type" attribute we set previously to set the CSS class of our node in a custom HTML helper template.
@model MvcSiteMapProvider.Web.Html.Models.SiteMapNodeModel
@using System.Web.Mvc.Html
@using MvcSiteMapProvider.Web.Html.Models
@if (Model.IsCurrentNode && Model.SourceMetadata["HtmlHelper"].ToString() != "MvcSiteMapProvider.Web.Html.MenuHelper") {
<text>@Model.Title</text>
} else if (Model.IsClickable) {
if (string.IsNullOrEmpty(Model.Description))
{
if (!Model.Attributes.ContainsKey("type"))
{
<a href="@Model.Url">@Model.Title</a>
}
else
{
<a href="@Model.Url" class="@Model.Attributes["type"].ToString()">@Model.Title</a>
}
}
else
{
if (!Model.Attributes.ContainsKey("type"))
{
<a href="@Model.Url" title="@Model.Description">@Model.Title</a>
}
else
{
<a href="@Model.Url" title="@Model.Description" class="@Model.Attributes["type"].ToString()">@Model.Title</a>
}
}
} else {
<text>@Model.Title</text>
}
When a custom attribute is added via XML, MvcSiteMapNodeAttribute, Dynamic Node Provider, or ISiteMapNodeProvider, it is added to the shared cache that is available for every HTTP request for any user.
However, when a custom attribute is set using the static MvcSiteMapProvider.SiteMaps class, the value that is set will exist for the current HTTP request only. This can be useful if you have user-specific data and you want to override or set the value for the current request only. This will not affect any other user on the web site.
MvcSiteMapProvider.SiteMaps.Current.CurrentNode.Attributes["type"] = "special-equipment";
In addition to setting values for the current request, you can also delete a value for the current request and it will automatically be restored to the state of the shared cache when the request is finished processing.
MvcSiteMapProvider.SiteMaps.Current.CurrentNode.Attributes.Remove("type");
Since the datatype of the Attributes dictionary is IDictionary<string, object>
, you can use it to attach your own custom objects to any node. This makes it possible to extend the SiteMapNode object to meet many custom requirements.
public class MyCustomData
{
public string MyName { get; set; }
public string MyValue { get; set; }
}
MvcSiteMapProvider.SiteMaps.Current.CurrentNode.Attributes.Add("someCustomData", new MyCustomData { MyName = "Some Name", MyValue = "Some Value" });
Note that care must be taken if you decide to add your own objects from Dynamic Node Provider or ISiteMapNodeProvider that you do not change the values in your objects (unless it is intended and you do your own thread locking) because the change will affect every user on the web site. A good practice to follow is to make your properties only settable from the constructor of your custom object so they cannot be changed accidentally.
public class MyCustomData
{
public MyCustomData(string myName, string myValue)
{
this.MyName = myName;
this.MyValue = myValue;
}
public string MyName { get; private set; }
public string MyValue { get; private set; }
}
public class ProductDynamicNodeProvider
: DynamicNodeProviderBase
{
public override IEnumerable<DynamicNode> GetDynamicNodeCollection(ISiteMapNode node)
{
using (var db = new MyEntities())
{
// Create a node for each product
foreach (var product in db.Products)
{
var dynamicNode = new DynamicNode();
dynamicNode.Title = product.Name;
dynamicNode.ParentKey = "Category_" + product.CategoryId;
dynamicNode.RouteValues.Add("id", product.Id);
dynamicNode.Attributes.Add("someCustomData", new MyCustomData(product.MyName, product.MyValue));
yield return node;
}
}
}
}
Want to contribute? See our Contributing to MvcSiteMapProvider guide.
- Upgrading from v3 to v4
- Routing Basics
- Configuring MvcSiteMapProvider
- Defining Sitemap Nodes in XML
- Defining Sitemap Nodes using .NET Attributes
- Defining Sitemap Nodes using IDynamicNodeProvider
- HtmlHelper Extensions
- Controlling URL Behavior
- Using Action Filter Attributes
- Sitemaps XML Protocol Endpoint for Search Engines
- Using Custom Attributes on a Node
- Specifying Node Order
- Advanced Node Visibility
- Multiple Navigation Paths to a Single Page
- Multiple Sitemaps in One Application
- Security Trimming
Other places around the web have some documentation that is helpful for getting started and finding answers that are not found here.
- MvcSiteMapProvider 4.0 - A Test Drive
- MvcSiteMapProvider 4.0 - SEO Features Tutorial
- How to Make MvcSiteMapProvider Remember a User’s Position
- MvcSiteMapProvider 4.0 - Cache Configuration
- MvcSiteMapProvider 4.0 - Extending the Cache
- MvcSiteMapProvider 4.0 - Unit Testing with the SiteMaps Static Methods
- Debugging an MvcSiteMapProvider Configuration
- Converting from C# to Vb MvcSiteMapProvider
- ASP.NET MVC Menu using Site Map Provider & Bootstrap 3 Navbar
- ASP.NET MVC SiteMapPath using Site Map Provider & Bootstrap Breadcrumbs
- NightOwl888's MvcSiteMapProvider Demos - Filter for "MvcSiteMapProvider" to see the most relevant.
- MvcSiteMapProvider Tutorial and Examples
- MvcSiteMapProvider Tutorial 2 - Breadcrumbs
- Getting Started with MvcSiteMapProvider
- Inside the MvcSiteMapProvider - Part 1
- Inside the MvcSiteMapProvider - Part 2: Dynamic node providers
- Inside the MvcSiteMapProvider - Part 3: The ISiteMapVisibilityProvider
- Inside the MvcSiteMapProvider - Part 4: The IAclModule
- Inside the MvcSiteMapProvider - Part 5: The ISiteMapNodeUrlResolver
- Styling MvcSiteMapProvider with CSS
- Using MvcSiteMapProvider with Twitter Bootstrap
- ASP.NET MVC Menu using Site Map Provider & Bootstrap Navbar