<input id="ohw05"></input>
  • <table id="ohw05"><menu id="ohw05"></menu></table>
  • <var id="ohw05"></var>
  • <code id="ohw05"><cite id="ohw05"></cite></code>
    <label id="ohw05"></label>
    <var id="ohw05"></var>
  • 基于.NetCore開發博客項目 StarBlog - (8) 分類層級結構展示

    系列文章

    前言

    前面 (6) 頁面開發之博客文章列表 介紹了文章列表的開發,頁面中左側是分類列表,右側是該分類下的文章,這個布局乍看還是不錯的,不過考慮到本項目支持多級分類,但分類列表只會機械式的把所有分類都顯示出來,無法體現分類的層級結構且占用了很大的頁面縱向空間,因此本文將對分類列表進行改造,使之能夠體現多級分類、節省頁面空間。

    關于樹形結構組件,我找了一圈,適配bootstrap(基于jQuery)的組件很難找,大都是很老的,只找到了bootstrap-treeview這個稍微好用一點的,看了下GitHub項目主頁,同樣是好久沒更新了,它適配的甚至是3.x版本的bootstrap,現在都已經2022年了,bootstrap都更新到5.x版本了,然而沒找到更好的,湊合用吧~ (實在不行還能把它代碼clone下來魔改)

    安裝

    這個組件是比較老的

    依賴bower,如果沒有bower的話需要先安裝

    npm install -g bower
    

    然后在StarBlog.Web目錄下執行以下命令安裝依賴

    npm install bootstrap-treeview
    

    因為我們的靜態資源都在wwwroot下,所以npm安裝的前端資源還需要通過gulp工具自動復制到wwwroot里,這一點在前面的文章中有介紹過,忘記的同學可以看一下前面這篇:基于.NetCore開發博客項目 StarBlog - (5) 開始搭建Web項目

    編輯gulpfile.js文件,在const libs配置中增加一行

    //使用 npm 下載的前端組件包
    const libs = [
        // ...
        {name: "bootstrap-treeview", dist: "./node_modules/bootstrap-treeview/dist/**/*.*"},
    ];
    

    然后執行gulp任務即可

    gulp move
    

    完成之后可以看到wwwroot/lib下已經多了一個bootstrap-treeview目錄了

    接下來我們就可以在頁面中引用

    用法

    正式開始前,先來了解一下這個組件的用法

    引入依賴

    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/lib/bootstrap-treeview/dist/bootstrap-treeview.min.js"></script>
    

    在網頁里放一個容器

    <div id="categories">
    

    根據官方例子,使用js激活組件

    const instance = $('#categories').treeview({
        data: collections,
    });
    

    collections格式如下

    const collections = [
        {
            text: 'Parent 1',
            href: '#parent1',
            nodes: [
                {
                    text: 'Child 1',
                    href: '#child1',
                    nodes: [
                        {
                            text: 'Grandchild 1',
                            href: '#grandchild1',
                        },
                        {
                            text: 'Grandchild 2',
                            href: '#grandchild2',
                        }
                    ]
                },
                {
                    text: 'Child 2',
                    href: '#child2',
                }
            ]
        },
        {
            text: 'Parent 2',
            href: '#parent2',
        },
        {
            text: 'Parent 3',
            href: '#parent3',
        },
        {
            text: 'Parent 4',
            href: '#parent4',
        },
        {
            text: 'Parent 5',
            href: '#parent5',
        }
    ];
    

    官網的默認效果

    image

    不過經過我的測試,官網這個例子在bootstrap5下是有些問題的,默認的圖標都顯示不出來。需要我們自定義一下,加上圖標配置就行,用到的圖標是我們之前的安裝的FontAwesome Icons

    const instance = $('#categories').treeview({
        data: collections,
        collapseIcon: "fa fa-caret-down",
        expandIcon: "fa fa-caret-right",
        emptyIcon: 'fa fa-circle-o',
    });
    

    處理分類數據

    為了方便使用這個組件,我們需要在后端把分類層級包裝成這個組件需要的形式。

    首先定義一個節點類

    public class CategoryNode {
        public string text { get; set; } = "";
        public string href { get; set; } = "";
        public List<CategoryNode>? nodes { get; set; }
    }
    

    然后在Services/CategoryyService.cs里新增一個方法,用來生成分類的樹結構,為了代碼編寫方便,我直接用遞歸來實現。

    public List<CategoryNode>? GetNodes(int parentId = 0) {
        var categories = _cRepo.Select
            .Where(a => a.ParentId == parentId).ToList();
    
        if (categories.Count == 0) return null;
    
        return categories.Select(category => new CategoryNode {
            text = category.Name,
            nodes = GetNodes(category.Id)
        }).ToList();
    }
    

    這樣輸出來的數據就是這樣

    [
        {
          "text": "Android開發",
          "href": "",
          "nodes": null
        },
        {
          "text": "AspNetCore",
          "href": "",
          "nodes": [
            {
              "text": "Asp-Net-Core學習筆記",
              "href": "",
              "nodes": null
            },
            {
              "text": "Asp-Net-Core開發筆記",
              "href": "",
              "nodes": null
            }
          ]
        }
    ]
    

    哦差點忘了還得給每個節點加上href參數

    寫死是不可能寫死的,ControllerBase實例默認帶有一個IUrlHelper類型的Url屬性,可以用其Link()方法實現地址路由解析。

    不過我們這個方法是寫在Service里,并沒有ControllerBase實例,這時只能用依賴注入的方式,不過我在Stack Overflow上看到一個說法是,AspNetCore3.x之后,用LinkGenerator更好。

    上代碼,先注冊服務

    builder.Services.AddHttpContextAccessor();
    

    然后依賴注入

    private readonly IHttpContextAccessor _accessor;
    private readonly LinkGenerator _generator;
    
    public CategoryService(IHttpContextAccessor accessor, LinkGenerator generator) {
        _accessor = accessor;
        _generator = generator;
    }
    

    修改上面那個GetNodes方法,在CategoryNode初始化器里加上

    href = _generator.GetUriByAction(
        _accessor.HttpContext!,
        nameof(BlogController.List),
        "Blog",
        new {categoryId = category.Id}
    )
    

    具體代碼可以看GitHub:https://github.com/Deali-Axy/StarBlog/blob/master/StarBlog.Web/Services/CategoryService.cs

    生成的鏈接形式是這樣的:

    {
        "text": "Android開發",
        "href": "http://localhost:5038/Blog/List?categoryId=2",
        "nodes": null
    }
    

    前端渲染

    數據準備好了,這時遇到一個問題,數據是要放到js中處理的,那我要用fetch之類的異步請求來獲取分類數據再顯示樹形分類嗎?這樣的好處是寫起來比較直觀,然而我們項目的博客網站是后端渲染,現在博客列表頁面混入了異步請求,會導致割裂感,右邊部分的文章列表服務端渲染出來在瀏覽器上展示了,左側的分類還要異步去請求。

    斟酌了一下,我決定這個分類也使用后端渲染,雖然有點反直覺,但根據bootstrap-treeview組件的文檔,它可以使用json方式渲染分類,那我只需要在后端把分類數據序列化成json格式,然后在view中渲染到js代碼中就行。

    開始吧~

    編輯StarBlog.Web/ViewModels/BlogListViewModel.cs文件,添加倆字段

    public List<CategoryNode> CategoryNodes { get; set; }
    // 將上面的分類層級數據轉換成Json字符串
    public string CategoryNodesJson => JsonSerializer.Serialize(
        CategoryNodes,
        new JsonSerializerOptions {Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping}
    );
    

    然后修改一下Controller,StarBlog.Web/Controllers/BlogController.cs,先依賴注入CategoryService

    然后修改List方法

    public IActionResult List(int categoryId = 0, int page = 1, int pageSize = 5) {
        var categories = _categoryRepo.Where(a => a.Visible)
            .IncludeMany(a => a.Posts).ToList();
        categories.Insert(0, new Category {Id = 0, Name = "All", Posts = _postRepo.Select.ToList()});
    
        return View(new BlogListViewModel {
            CurrentCategory = categoryId == 0 ? categories[0] : categories.First(a => a.Id == categoryId),
            CurrentCategoryId = categoryId,
            Categories = categories,
            // 增加這一行
            CategoryNodes = _categoryService.GetNodes(),
            Posts = _postService.GetPagedList(new PostQueryParameters {
                CategoryId = categoryId,
                Page = page,
                PageSize = pageSize,
                OnlyPublished = true
            })
        });
    }
    

    最后一步,修改View,StarBlog.Web/Views/Blog/List.cshtml,在底部加入js引用和一些js代碼,treeview組件的配置我已經封裝成initTreeView方法,可以直接使用。

    @section bottom {
        <script src="~/lib/jquery/dist/jquery.min.js"></script>
        <script src="~/lib/bootstrap-treeview/dist/bootstrap-treeview.min.js"></script>
        <script src="~/js/blog-list.js"></script>
        <script>
            const categories = '@Html.Raw(Model.CategoryNodesJson)'
            initTreeView(categories);
        </script>
    }
    

    View的關鍵代碼就這幾行,完整代碼可見:https://github.com/Deali-Axy/StarBlog/blob/master/StarBlog.Web/Views/Blog/List.cshtml

    最終效果

    完成之后的最終效果如下,算是支持了分類層級了,不過仍然不完美,存在幾個問題:

    • 不能高亮顯示當前所選分類
    • 沒有實現分類文章數量顯示(原來的版本是有的)
    • 無法自定義list-group-item樣式,存在下劃線不美觀
    • ...

    這幾個問題留著后面優化吧~ 暫時先折騰到這里…

    image

    博客項目的開發已經基本完成,項目代碼完全開源,有興趣的朋友可以點個star~

    參考資料

    posted @ 2022-05-24 23:07  程序設計實驗室  閱讀(432)  評論(0編輯  收藏  舉報
    国产美女a做受大片观看