在ASP.NET MVC下实现树形导航菜单
在需要处理很多分类以及导航的时候,树形导航菜单就比较适合。例如在汽车之家上:
页面主要分两部分,左边是导航菜单,右边显示对应的内容。现在,我们就在ASP.NET MVC 4 下临摹一个,如下:
实现的效果包括:
1、点击导航菜单上的品牌,左侧显示该品牌下的所有车型。
2、点击导航菜单上的车系,左侧显示该车系下的所有车型。
3、点击左侧上方的字母导航,锚点跳到导航菜单的对应部分。
4、页面加载完毕,显示所有品牌和车系,即树形导航完全展开。
5、点击导航菜单上的品牌,收缩或展开对应的车系,收缩时,品牌前面图标为+号,展开时,品牌前面的图片为-号。
......
源码部分,在这里。
思路呢?
页面分成左右2部分,使用Bootstrap轻松实现:
<div class="row"><div class="col-md-2 col-lg-2 col-sm-2"></div><div class="col-md-10 col-lg-10 col-sm-10"></div></div>
左侧最上方的字母导航,被放在一个div中,页面加载的时候向控制器动态请求。
品牌上方的字母归类,比如奥迪上方的字母A,实际上是一个div。
品牌和车系放在了ul中,比如奥迪品牌以及奥迪下的奥迪A4和奥迪A6车系。车系被放在了dl中。
树形菜单采用模版比较合适,先把数据填充到模版,再把模版追加到页面元素。
当点击左侧树形导航上的品牌或车系,右侧通过iframe来呈现对应的内容。
领域先行。有关品牌和车系就抽象成如下的类:
public class CarCategory{public int Id { get; set; }public int ParentId { get; set; }public string Name { get; set; }public string FirstLetter { get; set; }public string AnchorName { get; set; }public int Level { get; set; }public short DelFlag { get; set; }}
有关车型就抽象成如下的类:
public class Car{public int Id { get; set; }public int PinPaiId { get; set; }public int CheXiId { get; set; }public string Name { get; set; }}
页面左侧呈现树形导航需要向控制器请求json数据,大致格式是:
首字母
锚点名称
所有品牌
品牌编号
品牌名称
所有车系
车系编号
车系名称
车系下车型的总数量
貌似有3层,那就从最里面这层开始建模。有关车系在树形导航中的显示:
public class CheXiForDisplay{public int CheXiId { get; set; }public int TotalCount { get; set; }public string CheXiName { get; set; }}
有关品牌在树形导航中的显示:
public class PinPaiForDisplay{public int PinPaiId { get; set; }public string PinPaiName { get; set; }public List<CheXiForDisplay> CheXis { get; set; }}
有关品牌车系分组的:
public class PinPaiCheXiForDisplay{public string FirstLetter { get; set; }public string Anchor { get; set; }public List<PinPaiForDisplay> PinPais { get; set; }}
数据源从哪里来?模拟了一个:
public class Database{public static IEnumerable<CarCategory> GetAllCarCategories(){return new List<CarCategory>{new CarCategory(){Id = 1, ParentId = 0, Name = "奥迪",FirstLetter = "A", AnchorName = "AA", Level = 1, DelFlag = 0},new CarCategory(){Id = 2, ParentId = 0, Name = "宝马",FirstLetter = "B", AnchorName = "BB", Level = 1, DelFlag = 0},new CarCategory(){Id = 3, ParentId = 0, Name = "保时捷",FirstLetter = "B", AnchorName = "BB", Level = 1, DelFlag = 0},new CarCategory(){Id = 4, ParentId = 0, Name = "长安",FirstLetter = "C", AnchorName = "CC", Level = 1, DelFlag = 0},new CarCategory(){Id = 5, ParentId = 0, Name = "大众",FirstLetter = "D", AnchorName = "DD", Level = 1, DelFlag = 0},new CarCategory(){Id = 6, ParentId = 0, Name = "东风",FirstLetter = "D", AnchorName = "DD", Level = 1, DelFlag = 0},new CarCategory(){Id = 7, ParentId = 0, Name = "丰田",FirstLetter = "F", AnchorName = "FF", Level = 1, DelFlag = 0},new CarCategory(){Id = 8, ParentId = 0, Name = "福特",FirstLetter = "F", AnchorName = "FF", Level = 1, DelFlag = 0},new CarCategory(){Id = 9, ParentId = 1, Name = "奥迪A4",FirstLetter = "A", AnchorName = "AA", Level = 2, DelFlag = 0},new CarCategory(){Id = 10, ParentId = 1, Name = "奥迪A6",FirstLetter = "A", AnchorName = "AA", Level = 2, DelFlag = 0},new CarCategory(){Id = 11, ParentId = 2, Name = "宝马1",FirstLetter = "B", AnchorName = "BB", Level = 2, DelFlag = 0},new CarCategory(){Id = 12, ParentId = 2, Name = "宝马2",FirstLetter = "B", AnchorName = "BB", Level = 2, DelFlag = 0},new CarCategory(){Id = 13, ParentId = 3, Name = "保时捷1",FirstLetter = "B", AnchorName = "BB", Level = 2, DelFlag = 0},new CarCategory(){Id = 14, ParentId = 3, Name = "保时捷2",FirstLetter = "B", AnchorName = "BB", Level = 2, DelFlag = 0},new CarCategory(){Id = 15, ParentId = 4, Name = "长安1",FirstLetter = "C", AnchorName = "CC", Level = 2, DelFlag = 0},new CarCategory(){Id = 16, ParentId = 4, Name = "长安2",FirstLetter = "C", AnchorName = "CC", Level = 2, DelFlag = 0},new CarCategory(){Id = 17, ParentId = 5, Name = "大众1",FirstLetter = "D", AnchorName = "DD", Level = 2, DelFlag = 0},new CarCategory(){Id = 18, ParentId = 5, Name = "大众2",FirstLetter = "D", AnchorName = "DD", Level = 2, DelFlag = 1},new CarCategory(){Id = 19, ParentId = 6, Name = "东风1",FirstLetter = "D", AnchorName = "DD", Level = 2, DelFlag = 0},new CarCategory(){Id = 20, ParentId = 6, Name = "东风2",FirstLetter = "D", AnchorName = "DD", Level = 2, DelFlag = 0},new CarCategory(){Id = 21, ParentId = 7, Name = "丰田1",FirstLetter = "F", AnchorName = "FF", Level = 2, DelFlag = 0},new CarCategory(){Id = 22, ParentId = 7, Name = "丰田2",FirstLetter = "F", AnchorName = "FF", Level = 2, DelFlag = 0},new CarCategory(){Id = 23, ParentId = 8, Name = "福特1",FirstLetter = "F", AnchorName = "AFF", Level = 2, DelFlag = 0},new CarCategory(){Id = 24, ParentId = 8, Name = "福特2",FirstLetter = "F", AnchorName = "AFF", Level = 2, DelFlag = 0}};}public static IEnumerable<Car> GetAllCars(){return new List<Car>{new Car(){Id = 1, PinPaiId = 1, CheXiId = 9, Name = "奥迪A401"},new Car(){Id = 2, PinPaiId = 1, CheXiId = 9, Name = "奥迪A402"},new Car(){Id = 3, PinPaiId = 1, CheXiId = 10, Name = "奥迪A601"},new Car(){Id = 4, PinPaiId = 1, CheXiId = 10, Name = "奥迪A602"},new Car(){Id = 5, PinPaiId = 2, CheXiId = 11, Name = "宝马101"},new Car(){Id = 6, PinPaiId = 2, CheXiId = 11, Name = "宝马102"},new Car(){Id = 7, PinPaiId = 2, CheXiId = 12, Name = "宝马201"},new Car(){Id = 8, PinPaiId = 2, CheXiId = 12, Name = "宝马202"},new Car(){Id = 9, PinPaiId = 3, CheXiId = 13, Name = "保时捷101"},new Car(){Id = 10, PinPaiId = 3, CheXiId = 13, Name = "保时捷102"},new Car(){Id = 11, PinPaiId = 3, CheXiId = 14, Name = "保时捷201"},new Car(){Id = 12, PinPaiId = 3, CheXiId = 14, Name = "保时捷202"},new Car(){Id = 13, PinPaiId = 4, CheXiId = 15, Name = "长安101"},new Car(){Id = 14, PinPaiId = 4, CheXiId = 15, Name = "长安102"},new Car(){Id = 15, PinPaiId = 4, CheXiId = 16, Name = "长安201"},new Car(){Id = 16, PinPaiId = 4, CheXiId = 16, Name = "长安202"},new Car(){Id = 17, PinPaiId = 5, CheXiId = 17, Name = "大众101"},new Car(){Id = 18, PinPaiId = 5, CheXiId = 17, Name = "大众102"},new Car(){Id = 19, PinPaiId = 5, CheXiId = 18, Name = "大众201"},new Car(){Id = 20, PinPaiId = 5, CheXiId = 18, Name = "大众202"},new Car(){Id = 21, PinPaiId = 6, CheXiId = 19, Name = "东风101"},new Car(){Id = 22, PinPaiId = 6, CheXiId = 19, Name = "东风102"},new Car(){Id = 23, PinPaiId = 6, CheXiId = 20, Name = "东风201"},new Car(){Id = 24, PinPaiId = 6, CheXiId = 20, Name = "东风202"},new Car(){Id = 25, PinPaiId = 7, CheXiId = 21, Name = "丰田101"},new Car(){Id = 26, PinPaiId = 7, CheXiId = 21, Name = "丰田102"},new Car(){Id = 27, PinPaiId = 7, CheXiId = 22, Name = "丰田201"},new Car(){Id = 28, PinPaiId = 7, CheXiId = 22, Name = "丰田202"},new Car(){Id = 29, PinPaiId = 8, CheXiId = 23, Name = "福特101"},new Car(){Id = 30, PinPaiId = 8, CheXiId = 23, Name = "福特102"},new Car(){Id = 31, PinPaiId = 8, CheXiId = 24, Name = "福特201"},new Car(){Id = 32, PinPaiId = 8, CheXiId = 24, Name = "福特202"}};}}
好,现在可以向控制器要数据了。
public class HomeController : Controller{public ActionResult Index(){return View();}//获取所有首字母以及锚点的jsonpublic ActionResult GetFirstLettersJson(){var allCarCategories = Database.GetAllCarCategories();var result = from l in allCarCategoriesgroup l by l.FirstLetterinto gselect new {firstletter = g.Key, anchor=g.ToList()[0].AnchorName};return Json(result, JsonRequestBehavior.AllowGet);}//获取按首字母分组后的品牌车系jsonpublic ActionResult GetPinPaiCheXiJson(){var allPinPais = Database.GetAllCarCategories().Where(c => c.Level == 1).OrderBy(c => c.FirstLetter);var allPinPaisGroup = from p in allPinPaisgroup p by new{p.FirstLetter,p.AnchorName};List<PinPaiCheXiForDisplay> result1 = new List<PinPaiCheXiForDisplay>();foreach (var item in allPinPaisGroup){//品牌车系PinPaiCheXiForDisplay pinPaiCheXiForDisplay = new PinPaiCheXiForDisplay();pinPaiCheXiForDisplay.FirstLetter = item.Key.FirstLetter;pinPaiCheXiForDisplay.Anchor = item.Key.AnchorName;//品牌List<PinPaiForDisplay> pinPaiForDisplays = new List<PinPaiForDisplay>();foreach (var pinpai in item.ToList()){PinPaiForDisplay pinPaiForDisplay = new PinPaiForDisplay();pinPaiForDisplay.PinPaiId = pinpai.Id;pinPaiForDisplay.PinPaiName = pinpai.Name;//车系List<CheXiForDisplay> cheXiForDisplays = new List<CheXiForDisplay>();var cheXis = Database.GetAllCarCategories().Where(c => c.ParentId == pinpai.Id).OrderBy(c => c.Id);foreach (var chexi in cheXis){CheXiForDisplay cheXiForDisplay = new CheXiForDisplay();cheXiForDisplay.CheXiId = chexi.Id;cheXiForDisplay.CheXiName = chexi.Name;cheXiForDisplay.TotalCount = cheXis.Count();cheXiForDisplays.Add(cheXiForDisplay);}pinPaiForDisplay.CheXis = cheXiForDisplays;pinPaiForDisplays.Add(pinPaiForDisplay);}pinPaiCheXiForDisplay.PinPais = pinPaiForDisplays;result1.Add(pinPaiCheXiForDisplay);}return Json(result1, JsonRequestBehavior.AllowGet);}//根据品牌Id显示车型public ActionResult GetCheXingsByPId(int pid){var cars = Database.GetAllCars().Where(c => c.PinPaiId == pid);return View(cars);}//根据车系Id显示车型public ActionResult GetCheXingsByChexiId(int cxid){var cars = Database.GetAllCars().Where(c => c.CheXiId == cxid);return View(cars);}}
在Shared/_Layout.cshtml中,该引用的css,js都要引用上。
<head><meta charset="utf-8" /><meta name="viewport" content="width=device-width" /><title>@ViewBag.Title</title><link href="~/bootstrap/css/bootstrap.min.css" rel="stylesheet" />@Styles.Render("~/Content/css")@RenderSection("styles", required: false)@Scripts.Render("~/bundles/jquery")<script src="~/bootstrap/js/bootstrap.min.js"></script></head><body>@RenderBody()@RenderSection("scripts", required: false)</body>
Home/Index.cshtml就负责显示就行了。
@{ViewBag.Title = "Index";Layout = "~/Views/Shared/_Layout.cshtml";}@section styles{<link href="~/Content/sidemenu.css" rel="stylesheet" />}<div class="row"><div class="col-md-2 col-lg-2 col-sm-2"><!--字母导航开始--><div id="lDaoHang"></div><!--字母导航结束--><!--树开始--><div id="cTreeDiv" style="overflow-x: hidden; overflow-y: scroll; height: 550px; width: 99%;"></div><!--树结束--><div><dl id="test"></dl></div></div><div class="col-md-10 col-lg-10 col-sm-10"><div class="carContent" id="carContent"><iframe id="frameCar" src="" scrolling="no" frameborder="0" height="100%" width="100%" onload="this.height=this.contentWindow.document.documentElement.scrollHeight"></iframe></div></div></div>@section scripts{<script src="~/Scripts/jquery.tmpl.min.js"></script><script type="text/javascript">$(function () {//加载首字母$.getJSON('@Url.Action("GetFirstLettersJson", "Home")', function (data) {$('#firstLetterTemplate').tmpl(data).appendTo('#lDaoHang');});//加载所有品牌车系$.getJSON('@Url.Action("GetPinPaiCheXiJson", "Home")', function (data) {$('#pinpaiTemplate').tmpl(data).appendTo('#cTreeDiv');$('.pLink').each(function () {pinPaiInitialState($(this));});});//隐藏ifame所在div$("#carContent").css("display", "none");//点击品牌$('#cTreeDiv').on("click", ".pLink", function () {//切换togglePinPaiState($(this));//显示右边区域var url = "/Home/GetCheXingsByPId?pid=" + $(this).attr('id');$("#frameCar").attr("src", url);$("#carContent").css("display", "block");});//点击车系$('#cTreeDiv').on("click", ".cxLink", function () {//显示右边区域var url = "/Home/GetCheXingsByChexiId?cxid=" + $(this).attr('id');$("#frameCar").attr("src", url);$("#carContent").css("display", "block");});});//品牌的初始状态,即把车系隐藏和品牌前面的图标为+号var pstate = 0;//品牌只有2种状态:所有车系隐藏,品牌前面的图标变为为+号;要么显示品牌下的所有车系,品牌前面的图标变为-号//把车系隐藏和品牌前面的图标为+号,记为状态0//把车系隐藏和品牌前面的图标为-号,记为状态1function togglePinPaiState($pinpai) {if (pstate == 0) {var $i = $pinpai.parent().find("i");var attr = $i.attr('class');if (typeof attr !== typeof undefined && attr !== false) {$i.removeClass("iconHide");}$i.addClass("iconShow");$pinpai.parent().parent().find("dl").show();pstate = 1;} else {var $j = $pinpai.parent().find("i");var attr1 = $j.attr('class');if (typeof attr1 !== typeof undefined && attr1 !== false) {$j.removeClass("iconShow");}$j.addClass("iconHide");$pinpai.parent().parent().find("dl").hide();pstate = 0;}}function pinPaiInitialState($pinpai) {pstate = 0;togglePinPaiState($pinpai);}</script><!--首字母模版--><script id="firstLetterTemplate" type="text/x-jQuery-tmpl"><div class="lWrapper"><a href="#${anchor}" class="lLink">${firstletter}</a></div></script><!--品牌模版--><script id="pinpaiTemplate" type="text/x-jQuery-tmpl"><div class="lHeader" id="${Anchor}">${FirstLetter}</div><ul class="uTree">{{if PinPais}}{{each PinPais}}<li><h5 class="font-bold"><a href="javascript:void(0)" class="pLink" id="${$value.PinPaiId}"><i></i>${$value.PinPaiName}</a></h5><dl>{{tmpl(CheXis) "#chexiTemplate"}}</dl></li>{{/each}}{{else}}<li>没有对应品牌</li>{{/if}}</ul></script><!--车系模版--><script id="chexiTemplate" type="text/x-jQuery-tmpl"><dd><a id="${CheXiId}" href="javascript:void(0)" class="cxLink">${CheXiName}<em>(${TotalCount})</em></a></dd></script>}
以上,
○ 从控制器返回的有关树形菜单的json数据,先填充到jquery.tmpl.min.js模版中,然后追加到页面上。
○ 树形菜单的展开或收缩,通过全局变量pstate在0和1之间的切换来实现,页面初次加载给变量pstate一个初始值。
另外,点击树形导航上的品牌,iframe加载的视图是Home/GetCheXingsByPId.cshtml
@model IEnumerable<MvcApplication1.Models.Car>@{ViewBag.Title = "GetCheXingsByPId";Layout = "~/Views/Shared/_Layout.cshtml";}<h2>GetCheXingsByPId</h2><div>@foreach (var item in Model){<p>@item.Name </p>}</div>
点击树形导航上的车系,iframe加载的视图是Home/GetCheXingsByChexiId.cshtml
@model IEnumerable<MvcApplication1.Models.Car>@{ViewBag.Title = "GetCheXingsByChexiId";Layout = "~/Views/Shared/_Layout.cshtml";}<h2>GetCheXingsByChexiId</h2><div>@foreach (var item in Model){<p>@item.Name </p>}</div
就这样。
在ASP.NET MVC下实现树形导航菜单的更多相关文章
- ASP.NET MVC下的四种验证编程方式[续篇]
在<ASP.NET MVC下的四种验证编程方式>一文中我们介绍了ASP.NET MVC支持的四种服务端验证的编程方式("手工验证"."标注Validation ...
- ASP.NET MVC下的四种验证编程方式
ASP.NET MVC采用Model绑定为目标Action生成了相应的参数列表,但是在真正执行目标Action方法之前,还需要对绑定的参数实施验证以确保其有效性,我们将针对参数的验证成为Model绑定 ...
- Response.End()在Webform和ASP.NET MVC下的表现差异
前几天在博问中看到一个问题--Response.End()后,是否停止执行?MVC与WebForm不一致.看到LZ的描述后,虽然奇怪于为何用Response.End()而不用return方式去控制流程 ...
- ASP.NET MVC下的四种验证编程方式[续篇]【转】
在<ASP.NET MVC下的四种验证编程方式> 一文中我们介绍了ASP.NET MVC支持的四种服务端验证的编程方式(“手工验证”.“标注ValidationAttribute特性”.“ ...
- ASP.NET MVC下的四种验证编程方式【转】
ASP.NET MVC采用Model绑定为目标Action生成了相应的参数列表,但是在真正执行目标Action方法之前,还需要对绑定的参数实施验证以确保其有效 性,我们将针对参数的验证成为Model绑 ...
- ASP.NET MVC下使用AngularJs语言(六):获取下拉列表的value和Text
前面Insus.NET有在Angularjs实现DropDownList的下拉列表的功能.但是没有实现怎样获取下拉列表的value和text功能. 下面分别使用ng-click和ng-change来实 ...
- ASP.NET MVC下使用AngularJs语言(五):ng-selected
这次学习ng-selected语法,这个是为DropDownList下拉列表显示默认选项. 演示从下面步骤开始 1,新建一个model: 上面#14行代码的property,数据类型为bool.即是存 ...
- ASP.NET MVC下使用AngularJs语言(二):ng-click事件
程序用户交互,用户使用mouse点击,这是一个普通的功能. 在angularjs的铵钮点击命令是ng-click. 创建Angularjs的app使用前一篇<ASP.NET MVC下使用Angu ...
- ASP.NET MVC下使用AngularJs语言(一):Hello your name
新春节后,分享第一个教程. 是教一位新朋友全新学习ASP.NET MVC下使用AngularJs语言. 一,新建一个空的Web项目.使用NuGet下载AngularJs和jQuery.二,配置Bund ...
随机推荐
- Linux dd命令中dsync与fdatasync的区别【转】
在Linux系统中经常会使用dd命令来测试硬盘的写入速度,命令会涉及到两个参数:dsync与fdatasync,本文介绍一下其区别. dd if=/dev/zero of=/tmp/1Gbytes b ...
- app后端设计-- 数据库分表
当项目上线后,随着用户的增长,有些数据表的规模会以几何级增长,当数据达到一定规模的时候(例如100万条),查询,读取性能就下降得很厉害,这时,我们就要考虑分表. 更新表数据时会导致索引更新,当单表数据 ...
- Android Studio 3.0正式版填坑之路
原文:https://www.jianshu.com/p/9b25087a5d7d Android Studio 3.0启动图 序言 总看别人的文章,今天尝试着自己来写一篇.在逛论坛时候,无意间发 ...
- Codeforces Round #378 (Div. 2) F - Drivers Dissatisfaction
F - Drivers Dissatisfaction 题目大意:给你n个点,m条边,每个边都有一个权重w,每条边也有一个c表示,消耗c元可以把这条边的权重减1,求最多消耗s元的最小生成树. 思路:因 ...
- MapReduce原理2
MapReduce的shuffle机制 1.概述 mapreduce中,map阶段处理的数据如何传递给reduce阶段,是mapreduce框架中最关键的一个流程,这个流程就叫shuffle: shu ...
- 应用Mongoose开发MongoDB(2)模型(models)
数据模型及基础操作模板 为了使工程结构清晰,将数据模型(Schema, Model)的建立与增删查改的基础操作模板写在一起,命名为数据库设计中的Collection(对应于关系型数据库中的表定义)名, ...
- shiro实现账号同一时间只能在一处登录(非单点登录)
<bean id="myRealm" class="com.sys.shiro.MyRealm" /> <bean id="sess ...
- django邮件
1.邮件变量 (django settings.py) ADMINS = [('JOHN','JOHN@example.com'),('zhang','zhang@example.com')] #设置 ...
- [转]C++ STL list的初始化、添加、遍历、插入、删除、查找、排序、释放
list是C++标准模版库(STL,Standard Template Library)中的部分内容.实际上,list容器就是一个双向链表,可以高效地进行插入删除元素. 使用list容器之前必须加上S ...
- HDU3693 Math Teacher's Homework ---- 数位DP
HDU3693 Math Teacher's Homework 一句话题意 给定$n, k以及m_1, m_2, m_3, ..., m_n$求$x_1 \oplus x_2 \oplus x_3 \ ...