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的。