调用具有多个管道参数的模板

Del*_*ace 28 templates go go-templates

在Go模板中,有时将正确数据传递到正确模板的方式对我来说感觉很尴尬.使用管道参数调用模板看起来就像调用只有一个参数的函数.

假设我有一个关于Gophers的Gophers网站.它有一个主页主模板和一个实用程序模板来打印Gophers列表.

http://play.golang.org/p/Jivy_WPh16

输出:

*The great GopherBook*    (logged in as Dewey)

    [Most popular]  
        >> Huey
        >> Dewey
        >> Louie

    [Most active]   
        >> Huey
        >> Louie

    [Most recent]   
        >> Louie
Run Code Online (Sandbox Code Playgroud)

现在我想在子模板中添加一些上下文:在列表中以不同的方式格式化名称"Dewey",因为它是当前登录用户的名称.但我无法直接传递名称,因为只有一个可能的"点"参数管道!我能做什么?

  • 显然我可以将子模板代码复制粘贴到主模板中(我不想这样做,因为它降低了对子模板的所有兴趣).
  • 或者我可以使用访问器来处理某些全局变量(我也不想).
  • 或者我可以为每个模板参数列表创建一个新的特定结构类型(不是很好).

tux*_*21b 52

您可以在模板中注册一个"dict"函数,您可以使用该函数将多个值传递给模板调用.然后呼叫本身就是这样的:

{{template "userlist" dict "Users" .MostPopular "Current" .CurrentUser}}
Run Code Online (Sandbox Code Playgroud)

小"dict"帮助程序的代码,包括将其注册为模板函数,如下所示:

var tmpl = template.Must(template.New("").Funcs(template.FuncMap{
    "dict": func(values ...interface{}) (map[string]interface{}, error) {
        if len(values)%2 != 0 {
            return nil, errors.New("invalid dict call")
        }
        dict := make(map[string]interface{}, len(values)/2)
        for i := 0; i < len(values); i+=2 {
            key, ok := values[i].(string)
            if !ok {
                return nil, errors.New("dict keys must be strings")
            }
            dict[key] = values[i+1]
        }
        return dict, nil
    },
}).ParseGlob("templates/*.html")
Run Code Online (Sandbox Code Playgroud)

  • 我将其标记为已接受,因为恕我直言,它是管道化任意数据的“最先进的技术”(但仍然是关于 go 模板设计选择的解决方法)。这是我用 `dict()` 重新实现的整个示例:http://play.golang.org/p/oWdPlyWfvG (4认同)
  • 使用 `sprig` 定义了这个函数:http://masterminds.github.io/sprig/dicts.html (2认同)

val*_*val 5

您可以在模板中定义函数,并使这些函数成为在数据上定义的闭包,如下所示:

template.FuncMap{"isUser": func(g Gopher) bool { return string(g) == string(data.User);},}
Run Code Online (Sandbox Code Playgroud)

然后,您可以在模板中简单地调用此函数:

{{define "sub"}}

    {{range $y := .}}>> {{if isUser $y}}!!{{$y}}!!{{else}}{{$y}}{{end}}
    {{end}}
{{end}}
Run Code Online (Sandbox Code Playgroud)

操场上的此更新版本输出!!的当前用户大致相同:

*The great GopherBook*    (logged in as Dewey)

[Most popular]  

>> Huey
>> !!Dewey!!
>> Louie



[Most active]   

>> Huey
>> Louie



[Most recent]   

>> Louie
Run Code Online (Sandbox Code Playgroud)

编辑

由于可以在调用时覆盖函数Funcs,因此实际上可以在编译模板时预先填充模板函数,并使用如下所示的实际闭包来更新它们:

var defaultfuncs = map[string]interface{} {
    "isUser": func(g Gopher) bool { return false;},
}

func init() {
    // Default value returns `false` (only need the correct type)
    t = template.New("home").Funcs(defaultfuncs)
    t, _ = t.Parse(subtmpl)
    t, _ = t.Parse(hometmpl)
}

func main() {
    // When actually serving, we update the closure:
    data := &HomeData{
        User:    "Dewey",
        Popular: []Gopher{"Huey", "Dewey", "Louie"},
        Active:  []Gopher{"Huey", "Louie"},
        Recent:  []Gopher{"Louie"},
    }
    t.Funcs(template.FuncMap{"isUser": func(g Gopher) bool { return string(g) == string(data.User); },})
    t.ExecuteTemplate(os.Stdout, "home", data)
}
Run Code Online (Sandbox Code Playgroud)

虽然我不确定几个goroutine尝试访问同一模板时会如何运行...

工作示例