Go Web 编程入门:Go 模板

举报
宇宙之一粟 发表于 2022/05/16 10:54:00 2022/05/16
【摘要】 之前的文章学过把模板和视图分离,建立一个 Web 服务器来展现 HTML 模板。我们将学习如何使用 Go 的模板包创建动态 HTML 和文本文件。建立 Web 服务器到目前为止,我们一直在向终端输出模板,但是当我们开始深入研究更多 HTML 时,这开始变得不那么有意义了。相反,我们希望可视化在 Web 浏览器中生成的 HTML。为此,我们首先需要设置一个 Web 服务器来呈现我们的 HTML...


之前的文章学过把模板和视图分离,建立一个 Web 服务器来展现 HTML 模板。我们将学习如何使用 Go 的模板包创建动态 HTML 和文本文件。

建立 Web 服务器


到目前为止,我们一直在向终端输出模板,但是当我们开始深入研究更多 HTML 时,这开始变得不那么有意义了。相反,我们希望可视化在 Web 浏览器中生成的 HTML。为此,我们首先需要设置一个 Web 服务器来呈现我们的 HTML 模板。


package main

import (
	"html/template"
	"net/http"
)

var testTemplate *template.Template

type ViewData struct {
	Name string
}

func main() {

	var err error
	testTemplate, err = template.ParseFiles("hello.gohtml")
	if err != nil {
		panic(err)
	}

	http.HandleFunc("/", handler)
	http.ListenAndServe(":8000", nil)
}

func handler(w http.ResponseWriter, r *http.Request) {

	w.Header().Set("Content-Type", "text/html")

	vd := ViewData{"Kyrie Jobs"}

	err := testTemplate.Execute(w, vd)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}


创建一个名为 hello.gohtml 的文件并将以下内容添加到其中:

<h1>Hello, {{.Name}}!</h1>

现在通过在终端中输入 go run main.go 来启动服务器。该程序应保持运行并在端口 8000 上侦听 Web 请求,因此您可以在 localhost:8000 查看呈现的 HTML。




if...else 块


我们当前的模板很无聊,因为它只打印出一个人的名字。但是如果没有提供名字会发生什么?

让我们试试看。打开你的 main.go 文件并删除你的 handler() 函数中创建 ViewData 实例的代码,而是向 testTemplate.Execute 方法提供 nil 。完成后,您的 handler() 函数应如下所示:

func handler(w http.ResponseWriter, r *http.Request) {
  w.Header().Set("Content-Type", "text/html")

  err := testTemplate.Execute(w, nil)
  if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
  }
}


现在重新启动您的服务器(或让新服务器重新启动)并在浏览器中访问该页面 - localhost:8000。您应该看到一个看起来像这样的页面。


当我们不提供名称时,模板将使用空字符串代替值呈现。相反,我们希望我们的页面显示一个更通用的字符串,例如“Hello, there!”。让我们继续更新模板以使用我们的第一个操作,即 if/else 块。像这样更新 hello.gohtml:

<h1>Hello, {{if .Name}} {{.Name}} {{else}} there {{end}}!</h1>


如果您在浏览器中查看该页面,您应该会看到这更新了您的模板以显示“Hello, there !”就像我们想要的那样,但不幸的是,这在“there”这个词和感叹号之间增加了一个额外的空格。大多数时候这并不重要,但是在处理文本时,这有时会很烦人。在下一节中,我们将看看两个选项来稍微清理一下。


为了摆脱额外的空白,我们有几个选择:

  1. 从我们的模板中删除它们。

  2. 使用减号 (-) 告诉模板包修剪多余的空白。


第一个选项非常简单。我们只需将 hello.gohtml 文件更新为并删除多余的空格。


<h1>Hello, {{if .Name}}{{.Name}}{{else}}there{{end}}!</h1>

在这个例子中,这很好用,因为它是一段非常短的文本,但是想象一下我们正在生成 python 代码,其中间距很重要 - 这很快就会变得非常烦人。幸运的是,模板包还提供了一种使用减号来修剪不需要的空白的方法。


<h1>
  Hello,
  {{if .Name}}
    {{.Name}}
  {{- else}}
    there
  {{- end}}!
</h1>

在此代码片段中,我们通过将减号字符放在 else 关键字的前面来告诉模板包,我们不希望 Name 变量及其后面的任何内容之间的所有空格,并且我们也对 end 执行相同操作倒数第二行的关键字。重新加载您的页面,您应该会看到该空格不再存在。


对于本教程的其余部分,我将选择使用此处的第一个示例作为我的 hello.html 文件。


范围块

现在让我们假设您想在您的网站上显示所有小部件以及它们的价格。这是动态 Web 应用程序的任务类型,因为没有人愿意为您销售的每件商品手动创建 HTML 并维护它。相反,我们希望对每个项目使用相同的 HTML。在 Go 中,您可以使用模板内的范围块来实现此目的。


{{range .Widgets}}
  <div class="widget">
    <h3 class="name">{{.Name}}</h3>
    <span class="price">${{.Price}}</span>
  </div>
{{end}}



如果你重启你的服务器(或者让新的)并在 localhost:8000 重新加载页面,你现在应该会看到一个 HTML 页面,其中显示了三个小部件,每个小部件都有一个标题和一个价格。如果我们在数组中添加更多小部件,我们会在这里看到更多,如果我们将其保留为空数组,我们将不会在此处看到任何小部件。


range 操作混淆的最常见来源是我们正在访问小部件的各个属性,而无需在 .Widgets 值内使用索引或任何其他访问器。这是因为范围操作会将集合中每个对象的值设置为范围块内的点 (.)。例如,如果您要在范围块内渲染 {{.}},您将看到与在 Widget 对象上使用 fmt.Println() 相同的输出。


嵌套模板


随着您的模板开始增长,您会很快发现您需要在不同的地方重用组件。这就是嵌套模板来拯救这一天的地方。使用 Go 的模板包,您可以声明多个唯一命名的模板,然后当您需要在代码中使用另一个模板时,您只需使用 template 关键字引用它。例如,假设您想为您的网站声明一个页脚,您可以将其包含在多个页面和多个布局中。将以下页脚模板添加到 hello.html 文件中。你把它放在哪里并不重要,但我更喜欢把它放在文件的顶部。


{{define "footer"}}
  <footer>
    <p>
      Copyright 2016 Calhoun.io
    </p>
    <p>
      Contact information: <a href="mailto:jon@calhoun.io">jon@calhoun.io</a>.
    </p>
  </footer>
{{end}}

然后在小部件的范围块之后插入以下行。


{{template "footer"}}

您的 hello.gohtml 文件应如下所示:

{{define "footer"}}
  <footer>
    <p>
      Copyright 2016 Calhoun.io
    </p>
    <p>
      Contact information: <a href="mailto:jon@calhoun.io">jon@calhoun.io</a>.
    </p>
  </footer>
{{end}}

{{range .Widgets}}
  <div class="widget">
    <h3 class="name">{{.Name}}</h3>
    <span class="price">${{.Price}}</span>
  </div>
{{end}}

{{template "footer"}}

现在,如果您查看 localhost:8000,您将看到该页面正在使用您定义的页脚模板。当您定义一个模板时,您可以在任何其他模板中使用它,甚至可以多次使用它。尝试包含页脚模板两次以了解我的意思。


模板变量

我们的上一个示例很棒,但是当您需要在嵌套模板中包含一些数据时会发生什么?幸运的是,模板操作允许您传入第二个参数,该参数将分配给模板内的点 (.) 参数。例如,假设我们想为小部件的名称标题部分编写模板,我们可以使用以下代码来实现。

{{define "widget-header"}}
  <h3 class="name">{{.}}</h3>
{{end}}

{{range .Widgets}}
  <div class="widget">
    {{template "widget-header" .Name}}
    <span class="price">${{.Price}}</span>
  </div>
{{end}}

在这种情况下,.Name 属性被分配给 widget-header 模板内的点 (.) 属性。


带有模板变量的嵌套模板甚至允许您深入多层,这意味着可以从模板内部调用模板。


{{define "widget"}}
  <div class="widget">
    {{template "widget-header" .Name}}
    <span class="price">${{.Price}}</span>
  </div>
{{end}}

{{define "widget-header"}}
  <h3 class="name">{{.}}</h3>
{{end}}

{{range .Widgets}}
  {{template "widget" .}}
{{end}}

这段代码的最终结果是相同的,但是现在我们有了一个小部件模板,我们可以轻松地在 Web 应用程序的其他页面上重用它,而无需重写代码。


接下来


凭借您新学到的模板技能,您应该可以创建可重用的动态模板。在下一篇文章中,我们将介绍如何使用内置的模板函数,如 andeqindex,然后我们将看看如何添加我们自己的自定义函数。我原本打算包括那些听到的,但这篇文章有很多要介绍的行动,我不想卖空任何一个。


在关于函数的帖子之后,我们将介绍如何使用模板来创建 Web 应用程序的视图层。这将包括创建共享布局、定义可以被覆盖的默认模板,以及在不同页面中包含相同的模板,而无需将所有代码放入单个文件中。


如果您感到雄心勃勃或好奇,您还可以查看 text/templatehtml/template 的模板文档。继续自己探索其他一些知识~

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。