Pass data between templates [duplicate]
Asked Answered
I

2

5

I have simple case, where a templates (text/templates) includes another like this

`index.html`

{{ template "image_row" . }}


`image_row.html`

{{ define "image_row" }}

   To stuff here

{{ end }}

Now I want to reuse the image row template. Let's say I would like to pass a simple number, so that the image_row template builds up rows according to this number

I'd like to have something like that (where 5 is the additional argument)

index.html

{{ template "image_row" . | 5 }}

How could I achieve that in this case?

Iodism answered 15/4, 2017 at 12:25 Comment(1)
Please, clarify: your question is about text/template ? Because text/tempate allows nested template definitions.Lennyleno
S
7

I'm not sure whether there exists a builtin solution for passing multiple arguments to a template invocation but, in case there isn't one, you could define a function that merges its arguments and returns them as a single slice value, then you can register that function and use it in the template invocation.

Something like:

func args(vs ...interface{}) []interface{} { return vs }
t, err := template.New("t").Funcs(template.FuncMap{"args":args}).Parse...

Then, in your index.html, you would do this:

{{ template "image_row" args . 5 }}

And then inside your image_row template you can access the arguments with the builtin index function like this:

{{ define "image_row" }}

   To stuff here {{index . 0}} {{index . 1}}

{{ end }}

https://play.golang.org/p/gkdtvvJ1bb

Sadfaced answered 15/4, 2017 at 15:47 Comment(9)
Is that somehow possible to use with ParseFiles?Iodism
ParseFiles, ParseGlob... doesn't matter how you parse the template as longs as you've got the func registered with the Funcs method, the resulting template will have access to that func.Sadfaced
If you mean the function template.ParseFiles as opposed to the method *Template.ParseFiles then no, because you cannot reference a func that hasn't been yet registered. So you need to first create a template.Template with template.New then register the functions you want to use, and then call the ParseFiles method on that template.Sadfaced
at first, thanks for your help and sorry for making this more complicated then it should. For my case it does not working I made a non working example here play.golang.org/p/lJrfBs--d5 but never the less I think that is the right direction and I will find the solution sooner or later I guess :)Iodism
The snippet in the answer is just an approx example. ParseFiles returns two values not one, a *Template and an error value, so instead of t := ... do t, err := ... or wrap the whole ParseFiles call into the template.Must func as is in the playground example of my answer. I'll update the snippet to reflect this.Sadfaced
I got it working, here is also a good example goinbigdata.com/example-of-using-templates-in-golang Thank youIodism
No problem, I'm glad you got it working.Sadfaced
Is this thread-safe?Bandur
@Bandur Depends on what you mean exaclty, do you have a specific concern regarding specific value types? If you're passing non-pointer values around then it ought to be safe for concurrent use, if on the other hand you're passing around pointer values then you might run into issues, but Go's sync package provides you with enough tools to implement a concurrency-safe solution.Sadfaced
C
5

There is no builtin for this. You can add a function that creates a map and use that in the child template:

func argsfn(kvs ...interface{}) (map[string]interface{}, error) {
  if len(kvs)%2 != 0 {
    return nil, errors.New("args requires even number of arguments.")
  }
  m := make(map[string]interface{})
  for i := 0; i < len(kvs); i += 2 {
    s, ok := kvs[i].(string)
    if !ok {
        return nil, errors.New("even args to args must be strings.")
    }
    m[s] = kvs[i+1]
  }
  return m, nil
}

Add it the function to the template like this:

t := template.Must(template.New("").Funcs(template.FuncMap{"args": argsfn}).Parse(......

Use it like this:

{{template "image_row" args "row" . "a" 5}}{{end}}

{{define "image_row"}}
     {{$.row}} {{$.a}}
{{end}}

Run it in the playground

The advantage of using a map is that the arguments are "named". The advantage of using a slice as described in another answer is that the code is much simpler.

Centreboard answered 15/4, 2017 at 16:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.