上篇文章我们完成了 动态生成多级菜单 这个实用组件。
本篇文章我们要开发另一个实用组件:面包屑导航。
面包屑导航(BreadcrumbNavigation)这个概念来自童话故事"汉赛尔和格莱特",当汉赛尔和格莱特穿过森林时,不小心迷路了,但是他们发现在沿途走过的地方都撒下了面包屑,让这些面包屑来帮助他们找到回家的路。所以,面包屑导航的作用是告诉访问者他们目前在网站中的位置以及如何返回。(摘自百度百科)
要实现面包屑导航,也可以直接从nuget搜一些sitemap组件直接使用。
当然,和上篇一样,我们同样不用任何第三方组件,完全自己构建灵活通用的sitemap.
下面给出我的最佳实践。
文章提纲
-
概述要点
-
详细步骤
一、分析多级目录的html结构
二、根据html结构构建xml的站点地图源及相应的data model
三、构建html helper, 完成breadcrumb生成功能
四、前端调用
-
总结
概述要点
实现面包屑导航分成两个步骤:
1. 获取当前的url地址,根据url地址去相关配置文件中查询到当前位置,递归查询父节点
2. 根据查询到的结果动态拼接出 breadcrumb 的html
如果大家看了上篇文章应该会对这篇文章用到的技术很熟悉(比上篇文章更简单 : ))
我们直接根据站点的配置拼接出 html,前端通过自定义html helper的方法调用获取拼接出的内容。
关于如何自定义html helper,上篇文章有介绍,不再重复。
下面直接讲解详细步骤。
详细步骤
分成四个步骤
一、分析多级目录的html结构
首先打开一个样例,如下图
对应的html为
大家可以看到,由两层组成, 外面是一个<ol>, 里面是一组<li>。
每个<li>包含一个<a>标签,指向相应的位置地址。
最后的<li>不包含<a>标签,仅显示名字。
二、根据html结构构建xml的站点地图源及相应的data model
1. 根据上面的html结构,我们准备站点地图的数据源。
一般来说,站点地图不涉及到权限,也不会经常改变,如网站的某个位置没有配置,直接不显示即可,也不会有其他影响。
因此我们简单起见,直接准备个xml文档即可。
<?xml version="1.0" encoding="utf-8" ?>
<!--填绝对路径,类似 /XEngine/home/about-->
<MvcSiteMaps>
<MvcSiteMap ParnetID = "0" Name = "主页" Url = "/XEngine/" ID = "1" ></MvcSiteMap>
<MvcSiteMap ParnetID = "1" Name = "组织" Url = "/XEngine/Organization" ID = "2"></MvcSiteMap>
<MvcSiteMap ParnetID = "2" Name = "关于" Url = "/XEngine/home/about" ID = "3"></MvcSiteMap>
</MvcSiteMaps>
最终我们只需要将xml中相应的值填充到breadcrumb的html
2. 准备对应的data model
[XmlRoot("MvcSiteMaps")]
public class MvcSiteMaps
{
[XmlElement("MvcSiteMap")]
public MvcSiteMap[] Items { get; set; }
}
public class MvcSiteMap
{
[XmlAttribute(AttributeName = "ID")]
public int ID { get; set; }
[XmlAttribute(AttributeName = "Name")]
public string Name { get; set; }
[XmlAttribute(AttributeName = "Url")]
public string Url { get; set; }
[XmlAttribute(AttributeName = "ParnetID")]
public int ParnetID { get; set; }
public MvcSiteMap Parent { get; set; }
}
注意 [XmlAttribute(AttributeName = "xxx")],AttributeName需要和xml里面的名字对应,我们的xml和data model的命名完全对应,所以也可以省略。
三、构建html helper, 完成breadcrumb生成功能
需要完成以下几个功能
1. 获取xml中所有的节点信息
private static string SiteMapString = System.Configuration.ConfigurationManager.AppSettings["SiteMapString"] ?? string.Empty;
//获取sitemap的配置信息
public static IList<MvcSiteMap> GetSiteMapList()
{
using (TextReader reader = new StreamReader(HttpContext.Current.Server.MapPath(SiteMapString)))
{
var serializer = new XmlSerializer(typeof(MvcSiteMaps));
var items = (MvcSiteMaps)serializer.Deserialize(reader);
if (items != null)
{
return items.Items;
}
return null;
}
}
2. 根据上一步获取的节点信息及当前url地址,拼接出面包屑html
/// <summary>
/// 填充面包屑
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public static MvcHtmlString PopulateBreadcrumb(string url)
{
StringBuilder str = new StringBuilder();
List<string> pathList = new List<string>();
MvcSiteMap current = GetSiteMapList().FirstOrDefault(m=>m.Url==url);
GetParent(current, pathList);
pathList.Reverse();
for (int i = 0; i < pathList.Count; i++)
{
if (i == pathList.Count - 1)
{
string s = pathList[i];
s = s.Substring(s.IndexOf(">") + 1, s.LastIndexOf("<") - s.IndexOf(">") - 1);
str.AppendFormat("<li class='active'>{0}</li>", s);
}
else
{
str.AppendFormat("<li>{0}</li>", pathList[i]);
}
}
string result = str.ToString();
return MvcHtmlString.Create(result);
}
说明:首先找到当前位置,递归找出父节点依次添加到列表中;反转列表,完善html代码。
/// <summary>
/// 递归找到上一级
/// </summary>
/// <param name="parent"></param>
/// <param name="pathList"></param>
static void GetParent(MvcSiteMap parent, List<string> pathList)
{
if (parent != null)
{
pathList.Add(string.Format("<a href={0}>{1}</a>", parent.Url, parent.Name));
parent.Parent = GetSiteMapList().FirstOrDefault(i => i.ID == parent.ParnetID);
GetParent(parent.Parent, pathList);
}
}
四、前端调用
类似于上一篇文章,我们新建个html helper供前端调用。
这次我们稍微做一点改进(规范一下命名)
先看下微软原生的html helper定义方法,以Html.ActionLink为例,如下图
如方框处,类似于 xxxExtensions的命名,我们定义一个静态类来调用之前的方法。
前端调用:
以访问http://localhost/XEngine/home/about 为例,最终返回的结果
相应的html为:
<div>
<br />
<ol class="breadcrumb">
<li><a href=/XEngine/>主页</a></li><li><a href=/XEngine/Organization>组织</a></li><li class='active'>关于</li>
</ol>
</div>
总结
本篇对上篇的用到的html helper知识点做了细微改进 :
规范了自定义 html helper命名(类名为xxxExtensions和,原生形式统一);
直接返回MvcHtmlString类型,这样html字符串不会被转义,可以直接在前端调用。
自定义的 html helper非常实用,大家可以多多挖掘使用场景。
欢迎大家多多评论,祝学习进步:)
P.S.
示例中前端直接在_Layout.cshtml中使用。
后端菜单相关的程序结构: