Golang Html Template如何创建导航子菜单?转Go遇渲染难题
解决Golang Html Template嵌套导航子菜单问题
1. 先将扁平菜单数据转为树形结构
从数据库取出的菜单是扁平列表,必须先把它转换成父子嵌套的树形结构,才能让模板方便遍历渲染。
定义菜单结构体
type MenuItem struct { ID int ParentID int Name string URL string Children []*MenuItem // 存储子菜单 }
转换扁平数据为树形结构
假设你从数据库查询到的是[]MenuItem类型的扁平列表,用下面的方法构建树形结构:
func buildMenuTree(flatMenu []MenuItem) []*MenuItem { menuMap := make(map[int]*MenuItem) var rootMenu []*MenuItem // 先把所有菜单存入map,方便快速查找父菜单 for i := range flatMenu { item := &flatMenu[i] menuMap[item.ID] = item } // 遍历每个菜单,将子菜单挂载到对应父菜单下 for _, item := range menuMap { if item.ParentID == 0 { // ParentID为0的是主菜单,加入根列表 rootMenu = append(rootMenu, item) } else { // 找到父菜单,将当前菜单加入其子列表 if parent, exists := menuMap[item.ParentID]; exists { parent.Children = append(parent.Children, item) } } } return rootMenu }
2. 编写嵌套的Html模板
Go模板支持通过define定义子模板,然后递归调用自身实现嵌套渲染。
{{define "menu"}} <ul> {{range .}} <li> <a href="{{.URL}}">{{.Name}}</a> {{if gt (len .Children) 0}} {{template "menu" .Children}} <!-- 递归调用子模板渲染子菜单 --> {{end}} </li> {{end}} </ul> {{end}} <!-- 主模板 --> <!DOCTYPE html> <html> <head> <title>导航菜单</title> </head> <body> {{template "menu" .}} </body> </html>
3. 完整渲染示例
假设你已经从数据库拿到扁平菜单数据,构建树形结构后渲染模板:
package main import ( "html/template" "os" ) type MenuItem struct { ID int ParentID int Name string URL string Children []*MenuItem } func buildMenuTree(flatMenu []MenuItem) []*MenuItem { menuMap := make(map[int]*MenuItem) var rootMenu []*MenuItem for i := range flatMenu { item := &flatMenu[i] menuMap[item.ID] = item } for _, item := range menuMap { if item.ParentID == 0 { rootMenu = append(rootMenu, item) } else { if parent, exists := menuMap[item.ParentID]; exists { parent.Children = append(parent.Children, item) } } } return rootMenu } func main() { // 模拟数据库查询到的扁平菜单数据 flatMenu := []MenuItem{ {ID: 1, ParentID: 0, Name: "首页", URL: "/"}, {ID: 2, ParentID: 0, Name: "产品", URL: "/products"}, {ID: 3, ParentID: 2, Name: "产品A", URL: "/products/a"}, {ID: 4, ParentID: 2, Name: "产品B", URL: "/products/b"}, {ID: 5, ParentID: 0, Name: "关于我们", URL: "/about"}, {ID: 6, ParentID: 5, Name: "团队", URL: "/about/team"}, } // 构建树形菜单 menuTree := buildMenuTree(flatMenu) // 解析模板文件(假设模板文件名为menu.html) tpl := template.Must(template.ParseFiles("menu.html")) // 渲染模板 err := tpl.Execute(os.Stdout, menuTree) if err != nil { panic(err) } }
关键提示
- Go模板不像PHP可以在循环中动态查询父级,必须提前把数据整理成树形结构,这是实现嵌套的核心前提。
- 通过递归调用子模板,可以支持任意层级的菜单嵌套,不管是二级还是多级都能正常渲染。
内容的提问来源于stack exchange,提问作者Ugur Tas
相关产品推荐
相关产品推荐

