ASP.NET MVC中的Category的实现
最近做项目要实现一个分类的Category,点击第一级的节点出现此节点的下级节点,依次向下。这个功能的JQuery版本在js功能整理中已经有了,这里讲的是纯C#代码的实
现,还是有点难度的,现在来说一下我的实现方式吧。
首先是这种数据库方面,基本字段就是CategoryId(自增),ParentId,CategoryName,这三个是主要字段,CategoryName自不必说,然后CategoryId就是他的子节点的
ParentId,第一级节点的ParentId为0。
在程序中我们构建的ViewModel是这样的:
public class VMCategory { /// <summary> /// CategoryId /// </summary> public int CategoryId { get; set; } /// <summary> /// ParentId /// </summary> public int? ParentId { get; set; } /// <summary> /// CategoryName /// </summary> public string CategoryName { get; set; } /// <summary> /// 用来高亮被点击的节点 /// </summary> public string HighLight { get; set; } /// <summary> /// 下级节点集合 /// </summary> public List<VMCategory> CategoryNext { get; set; } }
现在有了我们的ViewModel,就要看具体的实现方法了,我们写了一个帮助类,通过给到的CategoryId来获得我们所需要的Category集合。
这里使用的是逐级附加的方式来实现对于整个Category列表的获取的。
首先添加一个总的List<VMCategory>叫finallyCategory,这个就是最终我们要返回的Category集合。
下面向finallyCategory内添加数据,我们分两部分来完成,第一步是添加非第一级Category的数据,第二步是添加Category的数据。
一:下级节点集合不是第一级节点
while(categoryId!=0)
{
1.根据节点的categoryId,获取到此节点的子节点集合,依次添加到临时的List<VMCategory> temporaryCategory中,如果(finallyCategory的父节点)==(当前节点的categoryId),此节点的CategoryNext=finallyCategory
2.将categoryId改成子节点的parentId,用来搜索当前节点的父节点的子节点,即他的兄弟节点
3.将temporaryCategory赋值给finallyCategory
}
二:下级节点集合是第一级节点(categoryId==0)
1.获取第一级节点集合,依次添加到临时的List<VMCategory> levelTopCategory中,如果(finallyCategory的父节点)==(当前节点的categoryId),此节点的CategoryNext=finallyCategory
2.将temporaryCategory赋值给finallyCategory
具体的代码实现如下:
public static List<VMCategory> GetCategoryListTest(int categoryId, CategoryBLL bll) { List<VMCategory> finallyCategory = new List<VMCategory>(); while (categoryId != 0) { List<VMCategory> temporaryCategory = new List<VMCategory>(); var cycleCategory = bll.RetriveByParentID(categoryId); if (categoryId != 0) categoryId = bll.RetriveByCategoryID(categoryId).FirstOrDefault().ParentID ?? 0; foreach (var c in cycleCategory) { VMCategory vmCategory = new VMCategory { CategoryId = c.CategoryID, CategoryName = c.CategoryName, ParentId = c.ParentID, CategoryNext = null, HighLight = "" }; if (finallyCategory.Count() != 0) { int cycleId = finallyCategory.FirstOrDefault().ParentId ?? 0; if (cycleId == c.CategoryID) { vmCategory.CategoryNext = finallyCategory; } } temporaryCategory.Add(vmCategory); } finallyCategory = temporaryCategory; } List<VMCategory> levelTopCategory = new List<VMCategory>(); foreach (var c in bll.RetriveByParentID(categoryId)) { VMCategory vmCategory = new VMCategory { CategoryId = c.CategoryID, CategoryName = c.CategoryName, ParentId = c.ParentID, CategoryNext = null }; if (finallyCategory.Count() != 0) { int cycleId = finallyCategory.FirstOrDefault().ParentId ?? 0; if (cycleId == c.CategoryID) { vmCategory.CategoryNext = finallyCategory; } } levelTopCategory.Add(vmCategory); } finallyCategory = levelTopCategory; return finallyCategory ; }
View:
<ul > @foreach (var levelOne in Model.LeftCategory) { <li > <a class="@levelOne.HighLight" href="@Url.Action("GetCategoryOrProductLeft", "Category", new { categoryid = @levelOne.CategoryId })" >@levelOne.CategoryName</a> @if (levelOne.CategoryNext != null) { <ul> @foreach (var levelTwo in levelOne.CategoryNext) { <li > <a class="@levelTwo.HighLight" href="@Url.Action("GetCategoryOrProductLeft", "Category", new { categoryid = @levelTwo.CategoryId })" >@levelTwo.CategoryName</a> @if (levelTwo.CategoryNext != null) { <ul> @foreach (var levelThree in levelTwo.CategoryNext) { <li > <a class="@levelThree.HighLight" href="@Url.Action("GetCategoryOrProductLeft", "Category", new { categoryid = @levelThree.CategoryId })" >@levelThree.CategoryName</a> @if (levelThree.CategoryNext != null) { <ul> @foreach (var levelFour in levelThree.CategoryNext) { <li > <a class="@levelFour.HighLight" href="@Url.Action("GetCategoryOrProductLeft", "Category", new { categoryid = @levelFour.CategoryId })" >@levelFour.CategoryName</a> </li> } </ul> } </li> } </ul> } </li> } </ul> } </li> } </ul>
这样的写法在前台赋值的时候有一个弊端,就是需要知道分类的最大层级是多少,才能去写几个循环,但是应为一般的category分类都不会太多,即使是添加分类,只是需要修改一下前台的view页面即可,可维护性还好。后期有新的改动,再做修改。
2013-6-8 修改,对于前台一大堆html的循环做了新的修改。一个递归的方法,用来形成html的StringBulider,最后被一个HtmlHelper的扩展方法调用,返回html代码。代码如下:(扩展方法没写)
private static StringBuilder CreateCategoryString(List<VMCategory> vmCategory, string requrl, int CategoryID=1) { StringBuilder tagul = new StringBuilder("<ul>");//创建<ul>标签 foreach(var v in vmCategory) { TagBuilder tagli = new TagBuilder("li");//创建<ul>标签 tagli.MergeAttribute("style", "margin-top:5px; list-style:none;"); TagBuilder taga = new TagBuilder("a");//创建<a>标签 taga.MergeAttribute("class", v.CategoryId == CategoryID ? "CategoryHighLight" :v.HighLight); taga.MergeAttribute("href", requrl + "/" + v.CategoryId.ToString()); taga.MergeAttribute("id",v.CategoryId.ToString()); taga.InnerHtml=v.CategoryName; tagul.Append(tagli.ToString()+taga.ToString()); //如果 v 含有子集 if(v.CategoryNext!=null) { tagul.Append(CreateCategoryString(v.CategoryNext, requrl, CategoryID)); } } tagul.Append("</ul>"); return tagul; }
这样的话,前台的调用就只需要一句代码
@Html.CategoryShow(Model.LeftCategory,Url.Action("GetCategoryOrProductLeft","Category"),-1)
第一个参数代表传回的CategoryList,第二个参数是不带categoryId的url地址,用来在方法里加工形成每个a标签的url的,第三个是被点击的categoryid,用来高亮category的。