什么是模板

你一定听说过一种叫做MVC的设计模式,Model处理数据,View展现结果,Controller控制用户的请求,至于View层的处理,在很多动态语言里面都是通过在静态HTML中插入动态语言生成的数据,例如JSP中通过插入<%=....=%>,PHP中通过插入<?php.....?>来实现的。

Web应用反馈给客户端的信息中的大部分内容是静态的,不变的,而另外少部分是根据用户的请求来动态生成的,例如要显示用户的访问记录列表。用户之间只有记录数据是不同的,而列表的样式则是固定的,此时采用模板可以复用很多静态代码。

Go模板使用

在Go语言中,我们使用template包来进行模板处理,使用类似ParseParseFileExecute等方法从文件或者字符串加载模板,然后执行类似上面图片展示的模板的merge操作。请看下面的例子:


func handler(w http.ResponseWriter, r *http.Request) {
    t := template.New("some template") //创建一个模板
    t, _ = t.ParseFiles("tmpl/welcome.html", nil)  //解析模板文件
    user := GetUser() //获取当前用户信息
    t.Execute(w, user)  //执行模板的merger操作
}

通过上面的例子我们可以看到Go语言的模板操作非常的简单方便,和其他语言的模板处理类似,都是先获取数据,然后渲染数据。

为了演示和测试代码的方便,我们在接下来的例子中采用如下格式的代码

  • 使用Parse代替ParseFiles,因为Parse可以直接测试一个字符串,而不需要额外的文件
  • 不使用handler来写演示代码,而是每个测试一个main,方便测试
  • 使用os.Stdout代替http.ResponseWriter,因为os.Stdout实现了io.Writer接口

模板中如何插入数据?

上面我们演示了如何解析并渲染模板,接下来让我们来更加详细的了解如何把数据渲染出来。一个模板都是应用在一个Go的对象之上,Go对象的字段如何插入到模板中呢?

字段操作

Go语言的模板通过{{}}来包含需要在渲染时被替换的字段,{{.}}表示当前的对象,这和Java或者C++中的this类似,如果要访问当前对象的字段通过{{.FieldName}},但是需要注意一点:这个字段必须是导出的(字段首字母必须是大写的),否则在渲染的时候就会报错,请看下面的这个例子:

package main

import (
        "html/template"
        "os"
)

type Person struct {
        Username string
        Email    string
}

func main() {
        t := template.New("fieldname example")

        t.Parse("hello {{.Username}}, {{.Email}}\n")
        p := Person{Username: "aceld", Email: "[email protected]"}
        t.Execute(os.Stdout, p)

}

上面的代码我们可以正确的输出hello aceld, [email protected],但是如果我们稍微修改一下代码,在模板中含有了未导出的字段,那么就会报错

type Person struct {
    UserName string
    email    string  //未导出的字段,首字母是小写的
}

t, _ = t.Parse("hello {{.UserName}}! {{.email}}")

上面的代码就会报错,因为我们调用了一个未导出的字段,但是如果我们调用了一个不存在的字段是不会报错的,而是输出为空。

如果模板中输出{{.}},这个一般应用于字符串对象,默认会调用fmt包输出字符串的内容。

输出嵌套字段内容

上面我们例子展示了如何针对一个对象的字段输出,那么如果字段里面还有对象,如何来循环的输出这些内容呢?我们可以使用{{with …}}…{{end}}{{range …}}{{end}}来进行数据的输出。

  • function (start, stop, step) {

          if(typeof stop === 'undefined') {
              stop = start;
              start = 0;
              step = 1;
          }
          else if(!step) {
              step = 1;
          }
    
          var arr = [];
          var i;
          if (step > 0) {
              for (i=start; i<stop; i+=step) {
                  arr.push(i);
              }
          } else {
              for (i=start; i>stop; i+=step) {
                  arr.push(i);
              }
          }
          return arr;
      } 这个和Go语法里面的range类似,循环操作数据
    
  • 操作是指当前对象的值,类似上下文的概念

详细的使用请看下面的例子:

package main

import (
        "html/template"
        "os"
)

type Friend struct {
        Fname string
}

type Person struct {
        UserName string
        Friends  []*Friend
        Emails   []string
}

func main() {

        t := template.New("field exzample")
        t.Parse(`hello {{.UserName}}!
                {{range .Emails}}
                        an email {{.}}
                {{end}}
                {{with .Friends}}
                {{range .}}
                        my friends name is {{.Fname}}
                {{end}}
                {{end}}
        `)

        f1 := Friend{Fname: "Gailun"}
        f2 := Friend{Fname: "Akali"}
        p := Person{UserName: "aceld",
                Emails:  []string{"[email protected]", "[email protected]"},
                Friends: []*Friend{&f1, &f2}}

        t.Execute(os.Stdout, p)

}

结果

hello aceld!

                        an email [email protected]

                        an email danbing_at@163.com



                        my friends name is Gailun

                        my friends name is Akali

results matching ""

    No results matching ""