[ASP.NET MVC] 產生 Bootstrap + Vue.js 多層式選單範本教學
在建構一個網站時,通常都會有選單來連結各頁面,常見的選單為一層或二層而已,結構簡單程式也比較好寫。
這次我要教學的是多層式的選單,算是比較複雜的選單邏輯,階層的數量由資料庫內決定,程式部份則由遞迴方式檢查階層深度,再輸出陣列到前端,前端使用 Bootstrap 樣式以及 Vue.js 綁定元件來呈現多層選單結果。
文章內程式碼為重點展示,完整範例可至文末連結下載喔。
Contents
建立專案
開啟 Visual Studio 2022,建立新專案為「ASP.NET Web 應用程式 (.NET Framework)」。
輸入專案名稱、位置之後,選擇「MVC」範本,並取消「設定 HTTPS」的勾選,就可以建立專案。
使用 Bootstrap 選單範本
在網路上有一個 Bootstrap 多層式選單的範本: Bootstrap Multi level Navbar Menu
(圖片來源: Bootstrap Multi level Navbar Menu)
這範本所使用的 js 版本有
https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js
https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js
ASP.NET MVC 預設的 jquery 及 bootstrap 3.4.1 版本與此範本的 js 版本相近,所以不需要升級調整。
我們就使用這個範本放到我們的專案裡面。
如果你找到的 Bootstrap 版本較新的話,那就需要先升級 ASP.NET MVC 預設的 Bootstrap 版本喔。
修改佈局檔 _Layout.cshtml
開啟專案的「\Views\Shared\_Layout.cshtml」檔案。
原本 _Layout.cshtml 裡面就有一個選單了,語法是 <div class="navbar navbar-inverse navbar-fixed-top">
開頭的,這裡我們要將原本的選單移除。
移除之後,可以放上我從範本修改的 HTML 語法,簡化一些階層。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
<div class="navbar navbar-default navbar-fixed-top" role="navigation"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">NavBar</a> </div> <div class="collapse navbar-collapse"> <ul class="nav navbar-nav navbar-right"> <li><a href="https://github.com/fontenele/bootstrap-navbar-dropdowns" target="_blank">GitHub Project</a></li> </ul> <ul class="nav navbar-nav"> <li class="active"><a href="#">Home</a></li> <li> <a href="#" class="dropdown-toggle" data-toggle="dropdown">Menu 1 <b class="caret"></b></a> <ul class="dropdown-menu multi-level"> <li><a href="#">Action</a></li> <li><a href="#">Another action</a></li> <li class="dropdown-submenu"> <a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown</a> <ul class="dropdown-menu"> <li><a href="#">Action</a></li> <li class="dropdown-submenu"> <a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown</a> <ul class="dropdown-menu"> <li class="dropdown-submenu"> <a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown</a> <ul class="dropdown-menu"> <li><a href="#">Action</a></li> <li><a href="#">Another action</a></li> </ul> </li> </ul> </li> </ul> </li> </ul> </li> </ul> </div><!--/.nav-collapse --> </div> </div> |
將範例的 CSS 語法直接放在 <head></head> 裡面,或是建立一個 menu.css 來存放 CSS 語法都行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
<style> .dropdown-submenu { position: relative; } .dropdown-submenu > .dropdown-menu { top: 0; left: 100%; margin-top: -6px; margin-left: -1px; -webkit-border-radius: 0 6px 6px 6px; -moz-border-radius: 0 6px 6px; border-radius: 0 6px 6px 6px; } .dropdown-submenu:hover > .dropdown-menu { display: block; } .dropdown-submenu > a:after { display: block; content: " "; float: right; width: 0; height: 0; border-color: transparent; border-style: solid; border-width: 5px 0 5px 5px; border-left-color: #ccc; margin-top: 5px; margin-right: -10px; } .dropdown-submenu:hover > a:after { border-left-color: #fff; } .dropdown-submenu.pull-left { float: none; } .dropdown-submenu.pull-left > .dropdown-menu { left: -100%; margin-left: 10px; -webkit-border-radius: 6px 0 6px 6px; -moz-border-radius: 6px 0 6px 6px; border-radius: 6px 0 6px 6px; } </style> |
完成後先看一下結果,按 F5 執行專案後,就會看到已經將多層選單取代原有的選單了。
目前是為了呈現多層式選單的樣式,接下來的重點就是建立資料庫及編寫 C# 語法,利用程式讀取資料庫的階層再重新呈現出來。
建立資料表
這裡用 SQL Server 做教學資料庫,尚未安裝 SQL Server 的話,可參考這篇文章: Windows Server 如何安裝 SQL Server 2019 免費開發版
開啟資料庫,我已建好教學資料庫 “Teach”,繼續建立新 Table,以下是新 Table Schema。
1 2 3 4 5 6 7 8 9 10 11 |
CREATE TABLE [dbo].[Menu]( [MenuID] [int] NOT NULL, [MenuName] [varchar](50) NOT NULL, [Url] [varchar](50) NOT NULL, [ParentID] [int] NOT NULL, CONSTRAINT [PK_Menu] PRIMARY KEY CLUSTERED ( [MenuID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY] ) ON [PRIMARY] GO |
我使用樹狀遞迴的結構來記錄選單階層關係。ParentID 欄位用來記錄上一層的 MenuID。
寫入測試資料
接下來用以下語法來當作多層選單的測試資料。
1 2 3 4 5 6 7 8 |
insert into [dbo].[Menu]([MenuID],[MenuName],[Url],[ParentID]) values (1,'Home','#',0) insert into [dbo].[Menu]([MenuID],[MenuName],[Url],[ParentID]) values (2,'Menu1','#',0) insert into [dbo].[Menu]([MenuID],[MenuName],[Url],[ParentID]) values (3,'Menu1-1','#',2) insert into [dbo].[Menu]([MenuID],[MenuName],[Url],[ParentID]) values (4,'Menu1-2','#',2) insert into [dbo].[Menu]([MenuID],[MenuName],[Url],[ParentID]) values (5,'Menu1-2-1','#',4) insert into [dbo].[Menu]([MenuID],[MenuName],[Url],[ParentID]) values (6,'Menu1-2-2','#',4) insert into [dbo].[Menu]([MenuID],[MenuName],[Url],[ParentID]) values (7,'Menu1-2-2-1','#',6) insert into [dbo].[Menu]([MenuID],[MenuName],[Url],[ParentID]) values (8,'Menu1-2-2-2','#',6) |
這測試資料是一個 4 層的選單結構。
編寫 Controller 語法
我們要程式在一開始就先讀取資料庫內的選單樹狀結構資料,會運用到遞迴的方法。
最後完成的資料會轉成 Json 格式回傳到前端去。
為求簡單示範,我程式都寫在 HomeController 裡面呼叫,了解基本程式邏輯後,可以將方法移至共用類別裡面。
開啟 \Controllers\HomeController.cs 檔案,我新增 GetMenu()
與 GetMenuRecursive()
兩個方法。
GetMenu()
是為了取回資料庫資料,GetMenuRecursive()
為了遞迴產生樹狀結構。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
/// <summary> /// 取得選單 /// </summary> /// <returns></returns> public string GetMenu() { // 樹狀結構物件 MenuList outModel = new MenuList(); outModel.Menus = new List<MenuItem>(); // 取得連線字串 string connStr = System.Web.Configuration.WebConfigurationManager.ConnectionStrings["ConnDB"].ConnectionString; // 當程式碼離開 using 區塊時,會自動關閉連接 using (SqlConnection conn = new SqlConnection(connStr)) { // 資料庫連線 conn.Open(); // 取得選單資料 string sql = "select * from Menu"; SqlCommand cmd = new SqlCommand(); cmd.CommandText = sql; cmd.Connection = conn; // 執行資料庫查詢動作 SqlDataAdapter adpt = new SqlDataAdapter(); adpt.SelectCommand = cmd; DataSet ds = new DataSet(); adpt.Fill(ds); DataTable dt = ds.Tables[0]; if (dt.Rows.Count > 0) { // 開始遞迴查詢子節點 this.GetMenuRecursive(dt, outModel.Menus, "0"); //0 是根節點 } } // 將物件轉為 Json 給前端使用 string json = JsonConvert.SerializeObject(outModel); return json; } /// <summary> /// 取得樹狀結構(遞迴模式) /// </summary> /// <param name="dt"></param> /// <param name="Menus"></param> /// <param name="ParentID"></param> public void GetMenuRecursive(DataTable dt, List<MenuItem> Menus, string ParentID) { // 查詢子節點 string filter = "ParentID = '" + ParentID + "'"; DataRow[] drs = dt.Select(filter); foreach (DataRow dr in drs) { // 選單項目 MenuItem menu = new MenuItem(); menu.MenuName = dr["MenuName"].ToString(); menu.Url = dr["Url"].ToString(); menu.Menus = new List<MenuItem>(); //加入子節點 Menus.Add(menu); // 遞迴查詢子節點 string MenuID = dr["MenuID"].ToString(); this.GetMenuRecursive(dt, menu.Menus, MenuID); } } |
連線資料庫字串語法放在 Web.Config 裡面,範例語法是:
1 2 3 |
<connectionStrings> <add name="ConnDB" connectionString="Data Source=127.0.0.1;Initial Catalog=Teach;Persist Security Info=false;User ID=test;Password=test;" providerName="System.Data.SqlClient"/> </connectionStrings> |
初始呼叫
程式首頁呼叫的 Controller 是 HomeController 的 Index()
動作。
所以我們測試範例就直接在 Index()
裡面呼叫選單方法,取回 Json 後回傳給前端。
在 public ActionResult Index()
裡面加入 ViewData["_MenuJson"] = this.GetMenu();
將取得的 Json 資料透過 ViewData 方式傳遞到前端去。
增加 Model
剛剛增加了新類別,因為這是選單相關的功能,所以我們增加一個 MenuModels 來存放相關類別。
在 Models 目錄按右鍵加入一個類別。
類別名稱為「MenuModels」。
然後在 MenuModels 類別裡面再加入新的類別。
1 2 3 4 5 6 7 8 9 10 11 |
public class MenuList { public List<MenuItem> Menus { get; set; } } public class MenuItem { public string MenuName { get; set; } public string Url { get; set; } public List<MenuItem> Menus { get; set; } } |
編寫 View 語法
我們剛剛已經在 _Layout.cshtml 增加了 Bootstrap 的選單語法,這裡就繼續在 _Layout.cshtml 裡面增加 Vue.js 的互動語法。
加入 Vue.js 套件
Vue.js 有提供 CDN 的 js 類別庫,可以加入在專案裡面。
在 @Scripts.Render("~/bundles/bootstrap")
底下加入 Vue.js 套件
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
有關 Vue.js 使用方法可至官網教學文件查看一下。
編寫 Vue.js 網頁控制語法
接下來要增加一段 Vue.js 語法,讓網頁在初始載入的時候,就取得 Json 資料,將 Json 轉為物件後,就可以綁定到 Bootstrap 語法裡面去。
在網頁的下方加入以下 Vue.js 語法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
<script> // 建立 Vue 選單元件 Vue.component('menu-tree', { name: 'menu-true' , template: `<ul class="dropdown-menu"> <template v-for="(item2, index) in input1"> <template v-if="item2.Menus.length === 0"> <li><a v-bind:href="item2.Url">{{item2.MenuName}}</a></li> </template> <template v-else> <li class="dropdown-submenu"> <a v-bind:href="item2.Url" class="dropdown-toggle" data-toggle="dropdown">{{item2.MenuName}}</a> <menu-tree v-bind:input1="item2.Menus"></menu-tree> </li> </template> </template> </ul>` , props: ['input1'] //傳入物件 }); var VueMenu = new Vue({ el: '#VueMenu' , data: { // 選單陣列 menuList: [] } // Vue 實體與掛載完成 , mounted: function () { var self = this; // 取得選單 Json var menuJson = '@Html.Raw(ViewData["_MenuJson"])'; // 將 Json 轉為物件 var menuArray = JSON.parse(menuJson).Menus; // 將物件傳到 Vue 資料上 self.menuList = menuArray; } }) </script> |
這裡會建立一個 Vue 的元件,元件裡面的 HTML 為選單項目,然後在裡面又有一個呼叫元件的語法 <menu-tree v-bind:input1="item2.Menus"></menu-tree>
這樣就形成了遞迴方式顯示多層選單。
修改 Bootstrap 選單
在上方之前增加的 Bootstrap 選單範本,開始要加入 Vue.js 語法來綁定資料,依資料庫陣列數量來顯示選單階層,將以下的語法取代上方 <div class="navbar navbar-default navbar-fixed-top" role="navigation">
語法內。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<div id="VueMenu"> <div class="navbar navbar-default navbar-fixed-top" role="navigation"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">NavBar</a> </div> <div class="collapse navbar-collapse"> <ul class="nav navbar-nav navbar-right"> <li><a href="https://github.com/fontenele/bootstrap-navbar-dropdowns" target="_blank">GitHub Project</a></li> </ul> <ul class="nav navbar-nav"> <!-- 開始建立選單--> <li v-for="(item1, index) in menuList"> <template v-if="item1.Menus.length === 0"> <a v-bind:href="item1.Url">{{item1.MenuName}}</a> </template> <template v-else> <a v-bind:href="item1.Url" class="dropdown-toggle" data-toggle="dropdown">{{item1.MenuName}} <b class="caret"></b></a> <!-- 呼叫 Vue 選單元件--> <menu-tree v-bind:input1="item1.Menus"></menu-tree> </template> </li> </ul> </div> </div> </div> </div> |
<li v-for="(item1, index) in menuList">
是 Vue.js 列表式的語法,用法可參考官方說明。
<menu-tree v-bind:input1="item1.Menus"></menu-tree>
是呼叫 Vue.js 元件的方式,用法可參考官方說明。
測試結果
都完成後就可以按 F5 測試一下結果。
程式所產生的選單階層就會由資料庫內所決定了,不管幾層都可以,程式會以遞迴方式產生選單。
重點整理
- 當選單階層多的時候可選用遞迴方式產生
- Bootstrap 提供許多選單範本
- 資料表欄位需要有上一層記錄
- 程式建立自我呼叫的遞迴方法
- Vue.js 遞迴需要用到元件
- 將 Vue.js 語法套用至 Bootstrap 範本裡面
範例下載
推薦課程
相關學習文章
- [ASP.NET MVC] 前台會員註冊範例教學 #CH1 (附範例)
- [ASP.NET MVC] 多國語系切換 – 使用資料庫管理與兼容 JavaScript 顯示(附範例)
- [ASP.NET MVC + Vue.js] 動態問卷表單製作教學
如果你在學習上有不懂的地方,需要諮詢服務,可以參考站長服務,我想辨法解決你的問題
如果文章內容有過時、不適用或錯誤的地方,幫我在下方留言通知我一下,謝謝
大大,您好
本專案使用Vue2,如要改成Vue3,語法不同,有哪些部份要修改(我自己修改,選單無法產生,能提供Vue3的版本嗎)。謝謝!