Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

text/template: Templates should be able to access variables outside them #17454

Closed
willfaught opened this issue Oct 15, 2016 · 7 comments
Closed

Comments

@willfaught
Copy link
Contributor

willfaught commented Oct 15, 2016

Templates cannot use variables defined outside them, like so:

{{$foo := .Foo}}
{{define "bar"}} {{$foo}} {{end}} {{/* error: unknown variable "foo" */}}

This means the template parameter pipeline must contain all the data needed by the template.

So if you've got a slice, and all the elements share some data, and in an iteration of the slice you want to pass everything about an element to a template, you can't factor that data out into a surrounding struct and then pull it into templates with variables:

type Names struct {
    First string
    Last []string
}

{{$first := .Names.First}}
{{range .Names.Last}} {{$first}} {{.}} {{end}} {{/* error: unknown variable "first" */}}

Note: See below for a correction.

You have to duplicate that shared data and push it down into each element of the slice:

type Name struct {
    First string
    Last string
}

{{range .Names}} {{.First}} {{.Last}} {{end}} {{/* .First is the same for all iterations */}}

I'm lazy and I don't want to do this. [Insert arguments here about DRY, compose-ability, simplicity, power, lexical scoping, etc.]

Note that this ability to reference outside variables isn't all that different from the ability to reference outside templates within the same "scope" (text/template doc term, not mine).

@quentinmit
Copy link
Contributor

Your second example works fine for me:
https://play.golang.org/p/ay4QlHnQFU

The first example doesn't work because you are defining a block. You can think of blocks as subroutines; the variables from one block do not leak into the scope of the second block. This is specifically documented:

A template invocation does not inherit variables from the point of its invocation.

@willfaught
Copy link
Contributor Author

Thanks for pointing that out. Yes, the second example should be inside a define block. I think they should be in scope at block definition, not when they're applied/run.

The scenario I had in mind:

type Names struct {
    First string
    Last []string
}

{{$first := .Names.First}}
{{define "names"}} {{range .}} {{$first}} {{.}} {{end}} {{end}}
[...]
{{template "names" .Names.Last}}

This won't work, but seems like it should, and it would be useful.

@quentinmit
Copy link
Contributor

Okay, so look at this example then:

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

In that example, if the contents of the block had been {{ $first }}, what should the code have printed?

Remember that templates can be invoked without invoking their enclosing code.

@willfaught
Copy link
Contributor Author

I'd expect a not-defined error, like it works now.

@willfaught
Copy link
Contributor Author

I think I missed your last sentence. Are you saying that a template defined in another template can be used without running the outer template?

@quentinmit
Copy link
Contributor

Yes, that's what my example attempts to demonstrate.

@willfaught
Copy link
Contributor Author

Is the template namespace global? Is the whole thing parsed before scanning for templates to register?

@golang golang locked and limited conversation to collaborators Oct 18, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants