Calling a template with several pipeline parameter

2019-01-07 10:14发布

In a Go template, sometimes the way to pass the right data to the right template feels awkward to me. Calling a template with a pipeline parameter looks like calling a function with only one parameter.

Let's say I have a site for Gophers about Gophers. It has a home page main template, and a utility template to print a list of Gophers.

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

Output :

*The great GopherBook*    (logged in as Dewey)

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

    [Most active]   
        >> Huey
        >> Louie

    [Most recent]   
        >> Louie

Now I want to add a bit of context in the subtemplate : format the name "Dewey" differently inside the list because it's the name of the currently logged user. But I can't pass the name directly because there is only one possible "dot" argument pipeline! What can I do?

  • Obviously I can copy-paste the subtemplate code into the main template (I don't want to because it drops all the interest of having a subtemplate).
  • Or I can juggle with some kind of global variables with accessors (I don't want to either).
  • Or I can create a new specific struct type for each template parameter list (not great).

9条回答
▲ chillily
2楼-- · 2019-01-07 10:45

The best i've found so far (and I don't really like it) is muxing and demuxing parameters with a "generic" pair container :

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

type PipelineDecorator struct {
    // The actual pipeline
    Data interface{}
    // Some helper data passed as "second pipeline"
    Deco interface{}
}

func decorate(data interface{}, deco interface{}) *PipelineDecorator {
    return &PipelineDecorator{
        Data: data,
        Deco: deco,
    }
}

I use this trick a lot for building my website, and I wonder if there exist some more idiomatic way to achieve the same.

查看更多
看我几分像从前
3楼-- · 2019-01-07 10:47

You can define functions in your template, and have these functions being closures defined on your data like this:

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

Then, you can simply call this function in your template:

{{define "sub"}}

    {{range $y := .}}>> {{if isUser $y}}!!{{$y}}!!{{else}}{{$y}}{{end}}
    {{end}}
{{end}}

This updated version on the playground outputs pretty !! around the current user:

*The great GopherBook*    (logged in as Dewey)

[Most popular]  

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



[Most active]   

>> Huey
>> Louie



[Most recent]   

>> Louie

EDIT

Since you can override functions when calling Funcs, you can actually pre-populate the template functions when compiling your template, and update them with your actual closure like this:

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)
}

Although I am not sure how that plays when several goroutines try to access the same template...

The working example

查看更多
Deceive 欺骗
4楼-- · 2019-01-07 10:49

I implemented a library for this issue which supports pipe-like arguments passing&check.

Demo

{{define "foo"}}
    {{if $args := . | require "arg1" | require "arg2" "int" | args }}
        {{with .Origin }} // Original dot
            {{.Bar}}
            {{$args.arg1}}
        {{ end }}
    {{ end }}
{{ end }}

{{ template "foo" . | arg "arg1" "Arg1" | arg "arg2" 42 }}
{{ template "foo" . | arg "arg1" "Arg1" | arg "arg2" "42" }} // will raise an error

Github repo

查看更多
登录 后发表回答