Bootstrap TreeView使用教程二:多級聯動效果的實現

接着上一篇博客繼續講,這裏我們要實現樹節點的多級聯動,效果如下所示
在這裏插入圖片描述
其實我們應該想到:理論上樹的層級是無限的,如果想要實現多級聯動,只有遞歸這一條路能走。我們先來看幾種情況:

情況一

在這裏插入圖片描述
根節點“中國”被選中,那麼此時“中國”節點下的所有子節點都應該被選中,換句話說這種情況只需要以“中國”爲出發節點向下遞歸就行了。

情況二

在這裏插入圖片描述
如果此時選中“浙江省”節點,那麼首先“浙江省”及其以下的子節點將會被全部選中,同時又由於“江蘇省”節點也已經被選中,因此在“浙江省”和“江蘇省”都被選中的情況下“中國”節點也需要被選中,換句話說這種情況需要進行兩次遞歸:第一次遞歸是向下遞歸,目的是選中該節點及其以下的子節點,第二次遞歸則是向上遞歸,目的是判斷該節點以上的根節點是否需要被選中。

情況三

在這裏插入圖片描述
情況三其實跟情況二差不多:如果此時選中“湖州市”,考慮到“杭州市”已經被選中,那麼“浙江省”也應該被選中,又考慮到“江蘇省”已經被選中,那麼“中國”也應該被選中。

情況四

在這裏插入圖片描述
如果撤銷對“中國”節點的選中狀態,那麼此時“中國”及其下屬的子節點都應該被撤銷選中狀態,換句話說這種情況只需要以“中國”爲起點向下遞歸即可。

情況五

在這裏插入圖片描述
如果撤銷“拱墅區”節點的選中狀態,則“杭州市”節點的選中狀態也應該撤銷,而這也將進一步導致“浙江省”和“中國”這兩個節點的選中狀態被撤銷,換句話說這種情況需要兩次遞歸:第一次是向下遞歸,目的是撤銷該節點及其子節點的選中狀態,第二次是向上遞歸,目的是找到根節點逐一撤銷其選中狀態。

主要用到的事件和方法

// 選中節點
onNodeChecked: function (event, node) {
    
},

// 撤銷節點選中
onNodeUnchecked: function (event, node) {
                    
}

// 獲取父節點
var parent = $('#tv').treeview('getParent', node);

// 根據nodeId選中某節點
$('#tv').treeview('checkNode', [node.nodeId, { silent: true }]);

// 根據nodeId撤銷某節點的選中狀態
$('#tv').treeview('uncheckNode', [node.nodeId, { silent: true }]);

// 獲取當前處於被選中狀態的所有節點 
var checkedNodes = $('#tv').treeview('getChecked');

// 獲取與某節點處於平級關係的兄弟節點
var brotherNodes = $('#tv').treeview('getSiblings', node);

前端代碼

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Bootstrap TreeView</title>
    <link href="Content/bootstrap.min.css" rel="stylesheet" />
    <link href="Content/bootstrap-treeview.min.css" rel="stylesheet" />
    <script src="Scripts/jquery-2.1.4.min.js"></script>
    <script src="Scripts/bootstrap.min.js"></script>
    <script src="Scripts/bootstrap-treeview.min.js"></script>
</head>
<body>
    <div id="tv" style="width:300px;border:1px solid #E8E8E8;margin-left:200px;margin-top:50px;"></div>
    <script>
        $(document).ready(function () {
            var nodeData = [];

            // 獲取後臺數據
            $.ajax({
                url: 'Handlers/GetTreeNodesHandler.ashx',
                type: 'post',
                dataType: 'json',
                async: false,
                success: function (data) {
                    nodeData = data;
                }
            })

            // 初始化TreeView
            $('#tv').treeview({
                data: nodeData,
                showCheckbox: true,
                showBorder: false,
                selectedBackColor: 'skyblue',
                selectedColor: 'white',
                onNodeSelected: function (event, node) {
                    alert(node.text);
                },
                onNodeChecked: function (event, node) {
                    check(node);
                },
                onNodeUnchecked: function (event, node) {
                    uncheck(node);
                }
            })
        })

        // 選擇節點
        function check(node) {
            var root = $('#tv').treeview('getParent', node);
            if (root.nodeId == undefined) {
                checkChildrenNodes(node);
            }
            else {
                checkChildrenNodes(node);
                checkParentNodes(node);
            }
        }

        // 取消選擇節點
        function uncheck(node) {
            var root = $('#tv').treeview('getParent', node);
            if (root.nodeId == undefined) {
                uncheckChildrenNodes(node);
            }
            else {
                uncheckChildrenNodes(node);
                uncheckParentNodes(node);
            }
        }

        // 向下遞歸:選擇某節點及以下的全部子節點
        function checkChildrenNodes(node) {
            if (node.nodes == null) {
                $('#tv').treeview('checkNode', [node.nodeId, { silent: true }]);
            }
            else {
                for (var i = 0; i < node.nodes.length; i++) {
                    $('#tv').treeview('checkNode', [node.nodeId, { silent: true }]);
                    checkChildrenNodes(node.nodes[i]);
                }
            }
        }

        // 向上遞歸:判斷某節點以上的根節點是否需要被選擇
        function checkParentNodes(node) {
            var checkedNodes = $('#tv').treeview('getChecked');
            var brotherNodes = $('#tv').treeview('getSiblings', node);
            if (brotherNodes.length > 0) {
                var checked = [];
                for (var i = 0; i < brotherNodes.length; i++) {
                    for (var j = 0; j < checkedNodes.length; j++) {
                        if (brotherNodes[i].nodeId == checkedNodes[j].nodeId) {
                            checked.push(true);
                        }
                    }
                }
                if (checked.length == brotherNodes.length) {
                    var parent = $('#tv').treeview('getParent', node);
                    $('#tv').treeview('checkNode', [parent.nodeId, { silent: true }]);
                    checkParentNodes(parent);
                }
            }
        }

        // 向下遞歸:取消選擇某節點及以下的全部子節點
        function uncheckChildrenNodes(node) {
            if (node.nodes == null) {
                $('#tv').treeview('uncheckNode', [node.nodeId, { silent: true }]);
            }
            else {
                for (var i = 0; i < node.nodes.length; i++) {
                    $('#tv').treeview('uncheckNode', [node.nodeId, { silent: true }]);
                    uncheckChildrenNodes(node.nodes[i]);
                }
            }
        }

        // 向上遞歸:取消選擇某節點以上的全部根節點
        function uncheckParentNodes(node) {
            var parent = $('#tv').treeview('getParent', node);
            if (parent.nodeId != undefined) {
                $('#tv').treeview('uncheckNode', [parent.nodeId, { silent: true }]);
                uncheckParentNodes(parent);
            }
        }
    </script>
</body>
</html>

後臺代碼

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Web;
using Newtonsoft.Json;

namespace WebApplication2.Handlers
{
    /// <summary>
    /// GetTreeNodesHandler 的摘要說明
    /// </summary>
    public class GetTreeNodesHandler : IHttpHandler
    {

        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/plain";

            // 查詢數據
            DataTable dataTable = new DataTable();
            string connectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ToString();
            using (SqlDataAdapter adapter = new SqlDataAdapter("select * from [TRegion]", connectionString))
            {
                adapter.Fill(dataTable);
            }

            // 轉換爲實體類
            List<Node> nodes = new List<Node>();
            foreach (DataRow row in dataTable.Rows)
            {
                Node node = new Node();
                node.id = Convert.ToInt32(row["id"].ToString());
                node.pid = Convert.ToInt32(row["pid"].ToString());
                node.text = row["text"].ToString();
                nodes.Add(node);
            }

            // 轉換爲JSON樹
            List<Node> list = CreateTreeNodes(nodes);
            context.Response.Write(JsonConvert.SerializeObject(list).Replace("[]", "null"));
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }

        // 生成樹
        private List<Node> CreateTreeNodes(List<Node> nodes)
        {
            List<Node> root = nodes.FindAll(node => node.pid == 0);
            return SortNodes(nodes, root);
        }

        // 遞歸分組
        private List<Node> SortNodes(List<Node> nodes, List<Node> root)
        {
            for (int i = 0; i < root.Count; i++)
            {
                List<Node> children = nodes.FindAll(node => node.pid == root[i].id);
                SortNodes(nodes, children);
                root[i].nodes = children;
            }
            return root;
        }
    }

    public class Node
    {
        /// <summary>
        /// 編號
        /// </summary>
        public int id { get; set; }

        /// <summary>
        /// 上一級編號
        /// </summary>
        public int pid { get; set; }

        /// <summary>
        /// 名稱
        /// </summary>
        public string text { get; set; }

        /// <summary>
        /// 子節點
        /// </summary>
        public List<Node> nodes;
    }
}

到此爲止,無限層級樹節點的多級聯動效果也實現了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章