• 03-Template Advance
    • 匿名组合
    • 模板继承
    • Links

    03-Template Advance

    学习完 第二章 之后,你对模板已经有了基本的认识

    本章将讨论 Go 的组合特性,以及 建立一个通用的调用 模板的方法

    本章的GitHub链接为: Source, Diff,
    Zip

    匿名组合

    匿名组合 其实是Go里的一个非常重要的特性,在 Go 的世界里没有继承,只有组合(当然还有接口)。组合其实可以实现部分的继承。

    main.go

    1. type Post struct {
    2. User
    3. Body string
    4. }
    5. // IndexViewModel struct
    6. type IndexViewModel struct {
    7. Title string
    8. User
    9. Posts []Post
    10. }

    就是 将 User User -> User其它都不变,这样执行,发现程序照常运行

    不过,现在我们可以改下templates/index.html

    templates.index.html

    1. <html>
    2. <head>
    3. {{if .Title}}
    4. <title>{{.Title}} - blog</title>
    5. {{else}}
    6. <title>Welcome to blog!</title>
    7. {{end}}
    8. </head>
    9. <body>
    10. <h1>Hello, {{.Username}}!</h1>
    11. {{range .Posts}}
    12. <div><p>{{ .Username }} says: <b>{{ .Body }}</b></p></div>
    13. {{end}}
    14. </body>
    15. </html>

    由于 匿名组合 ,我们现在可以将 {{.User.Username}} -> {{.Username}}

    就是我们可以直接使用 匿名组合 的属性,以及方法,其实也是变像的实现了继承。

    关于 Go 的 面向对象,可以看下 参考

    本小节 Diff

    模板继承

    其实 Go 的模板应该没有 Flask jinja2 这样的功能强大,它只有 include,所以为了实现模板的继承,我们需要发挥下主观能动性

    index.html

    1. package main
    2. import (
    3. "html/template"
    4. "io/ioutil"
    5. "net/http"
    6. "os"
    7. )
    8. // User struct
    9. type User struct {
    10. Username string
    11. }
    12. // Post struct
    13. type Post struct {
    14. User
    15. Body string
    16. }
    17. // IndexViewModel struct
    18. type IndexViewModel struct {
    19. Title string
    20. User
    21. Posts []Post
    22. }
    23. // PopulateTemplates func
    24. // Create map template name to template.Template
    25. func PopulateTemplates() map[string]*template.Template {
    26. const basePath = "templates"
    27. result := make(map[string]*template.Template)
    28. layout := template.Must(template.ParseFiles(basePath + "/_base.html"))
    29. dir, err := os.Open(basePath + "/content")
    30. if err != nil {
    31. panic("Failed to open template blocks directory: " + err.Error())
    32. }
    33. fis, err := dir.Readdir(-1)
    34. if err != nil {
    35. panic("Failed to read contents of content directory: " + err.Error())
    36. }
    37. for _, fi := range fis {
    38. f, err := os.Open(basePath + "/content/" + fi.Name())
    39. if err != nil {
    40. panic("Failed to open template '" + fi.Name() + "'")
    41. }
    42. content, err := ioutil.ReadAll(f)
    43. if err != nil {
    44. panic("Failed to read content from file '" + fi.Name() + "'")
    45. }
    46. f.Close()
    47. tmpl := template.Must(layout.Clone())
    48. _, err = tmpl.Parse(string(content))
    49. if err != nil {
    50. panic("Failed to parse contents of '" + fi.Name() + "' as template")
    51. }
    52. result[fi.Name()] = tmpl
    53. }
    54. return result
    55. }
    56. func main() {
    57. http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    58. u1 := User{Username: "bonfy"}
    59. u2 := User{Username: "rene"}
    60. posts := []Post{
    61. Post{User: u1, Body: "Beautiful day in Portland!"},
    62. Post{User: u2, Body: "The Avengers movie was so cool!"},
    63. }
    64. v := IndexViewModel{Title: "Homepage", User: u1, Posts: posts}
    65. templates := PopulateTemplates()
    66. templates["index.html"].Execute(w, &v)
    67. })
    68. http.ListenAndServe(":8888", nil)
    69. }

    templates/_base.html

    1. <html>
    2. <head>
    3. {{if .Title}}
    4. <title>{{.Title}} - blog</title>
    5. {{else}}
    6. <title>Welcome to blog!</title>
    7. {{end}}
    8. </head>
    9. <body>
    10. <div>Blog: <a href="/">Home</a></div>
    11. {{template "content" .}}
    12. </body>
    13. </html>

    templates/content/index.html

    1. {{define "content"}}
    2. <h1>Hello, {{.User.Username}}!</h1>
    3. {{range .Posts}}
    4. <div><p>{{ .User.Username }} says: <b>{{ .Body }}</b></p></div>
    5. {{end}}
    6. {{end}}

    这里用了模板继承,_base 是 基础模板,这样 比如 head 等信息不用再重复的去在每个.html文件中重复定义,我们可以专注于每个页面的业务逻辑和内容。

    由于没有像 Jinja2 这样的原生支持模板继承,这个实现的关键就是 PopulateTemplates 函数,它的作用是 遍历 templates/content/ 文件夹下的所有文件,并和 templates/_base.html 合成 template.Template,然后再存入 map 中(在 Python 中一般叫 dict),可以使用例如 index.html 的 key 来访问。

    我们现在运行下程序,页面还是和原来一样(只是我们在 _base template 里面加入了 Home 的导航),不过我们的templates文件夹已经有了基础模板,并且具备了快速扩展的能力。下章 Web Form 我们可以看见效果。

    03-01

    本小节 Diff

    • 目录
    • 上一节: 02-Template-Basic
    • 下一节: 04-Web-Form