02-Template Basic

学习完 第一章 之后,你已经拥有了一个简单,不过可以成功运行Web应用

本章将沿用这个应用,在此之上,加入模版渲染,使得页面更丰富

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

什么是模板

微博应用程序的主页会有一个欢迎用户的标题。虽然目前的应用程序还没有实现用户概念,但这不妨碍我使用一个 Go Stuct 来模拟一个用户,由于Go是静态语言,先定义 User 的结构,然后再初始化 user ,如下所示:

  1. type User struct {
  2. Username string
  3. }
  4. user := User{Username: "bonfy"}

创建模拟对象是一项实用的技术,它可以让你专注于应用程序的一部分,而无需为系统中尚不存在的其他部分分心。 在设计应用程序主页的时候,我可不希望因为没有一个用户系统来分散我的注意力,因此我使用了模拟用户对象,来继续接下来的工作。

原先的视图函数返回简单的字符串,我现在要将其扩展为包含完整HTML页面元素的字符串,如下所示:

  1. package main
  2. import (
  3. "html/template"
  4. "net/http"
  5. )
  6. // User struct
  7. type User struct {
  8. Username string
  9. }
  10. func main() {
  11. http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  12. user := User{Username: "bonfy"}
  13. tpl, _ := template.New("").Parse(`<html>
  14. <head>
  15. <title>Home Page - Bonfy</title>
  16. </head>
  17. <body>
  18. <h1>Hello, {{.Username}}!</h1>
  19. </body>
  20. </html>`)
  21. tpl.Execute(w, &user)
  22. })
  23. http.ListenAndServe(":8888", nil)
  24. }

对HTML标记语言不熟悉的话,建议阅读一下Wikipedia上的简介HTML Markup

利用上述的代码更新这个视图函数,然后再次运行 (这里并没有flask那样子的debug mode,每次更新都必须重新执行)

  1. $ go run main.go

在浏览器打开它的URL看看结果

02-01

本小节 Diff

我们暂时将 template 以文本方式耦合在代码中,随着我们项目的扩大,这个不利于管理,而且如果公司有前端支持的话,模板的工作很大可能会有前端设计,所以我们有必要对其进行解偶。我们打算将模板文件放入 templates 的文件夹

建立 templates 文件夹

  1. $ mkdir templates

将模板的内容移到 templates文件夹下

templates/index.html

  1. <html>
  2. <head>
  3. <title>Home Page - Bonfy</title>
  4. </head>
  5. <body>
  6. <h1>Hello, {{.Username}}!</h1>
  7. </body>
  8. </html>

然后将 template 文本从代码中移除

main.go

  1. package main
  2. import (
  3. "html/template"
  4. "net/http"
  5. )
  6. // User struct
  7. type User struct {
  8. Username string
  9. }
  10. func main() {
  11. http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  12. user := User{Username: "bonfy"}
  13. tpl, _ := template.ParseFiles("templates/index.html")
  14. tpl.Execute(w, &user)
  15. })
  16. http.ListenAndServe(":8888", nil)
  17. }

然后运行,在浏览器中打开,结果是与刚才一样的,不过代码的组织结构却更清晰了。

本小节 Diff

模板的常用操作

条件语句

在 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, {{.User.Username}}!</h1>
  11. </body>
  12. </html>

由于 User 没有 Title字段,所以我们引入新的 IndexViewModel 来具体处理所有与View对应的Model

main.go

  1. package main
  2. import (
  3. "html/template"
  4. "net/http"
  5. )
  6. // User struct
  7. type User struct {
  8. Username string
  9. }
  10. // IndexViewModel struct
  11. type IndexViewModel struct {
  12. Title string
  13. User User
  14. }
  15. func main() {
  16. http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  17. user := User{Username: "bonfy"}
  18. v := IndexViewModel{Title: "Homepage", User: user}
  19. tpl, _ := template.ParseFiles("templates/index.html")
  20. tpl.Execute(w, &v)
  21. })
  22. http.ListenAndServe(":8888", nil)
  23. }

本小节 Diff

循环

在 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, {{.User.Username}}!</h1>
  11. {{range .Posts}}
  12. <div><p>{{ .User.Username }} says: <b>{{ .Body }}</b></p></div>
  13. {{end}}
  14. </body>
  15. </html>

在 main.go 的 IndexViewModel 中加入 Post

  1. package main
  2. import (
  3. "html/template"
  4. "net/http"
  5. )
  6. // User struct
  7. type User struct {
  8. Username string
  9. }
  10. // Post struct
  11. type Post struct {
  12. User User
  13. Body string
  14. }
  15. // IndexViewModel struct
  16. type IndexViewModel struct {
  17. Title string
  18. User User
  19. Posts []Post
  20. }
  21. func main() {
  22. http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  23. u1 := User{Username: "bonfy"}
  24. u2 := User{Username: "rene"}
  25. posts := []Post{
  26. Post{User: u1, Body: "Beautiful day in Portland!"},
  27. Post{User: u2, Body: "The Avengers movie was so cool!"},
  28. }
  29. v := IndexViewModel{Title: "Homepage", User: u1, Posts: posts}
  30. tpl, _ := template.ParseFiles("templates/index.html")
  31. tpl.Execute(w, &v)
  32. })
  33. http.ListenAndServe(":8888", nil)
  34. }

本小节 Diff

运行效果图

02-02