Closed lukasjarosch closed 6 months ago
Great feature which will solve a lot of problems for us! 🚀
What was a bit tricky for me to understand at first is that there is a slight difference between supplying .
and .Inventory
as context to a template definition. .
is the whole context which includes the inventory (and .TargetName
) and .Inventory
is only the inventory. Depending on what you choose, in your partial template definition you either need to add a .Inventory
in before your references or you don't.
But that comes with a catch. In case I would invoke a template definition like this:
{{ template "dummy" .Inventory.commander }}
... I could no longer use the .TargetName
reference within that partial template. If you need the target name, you could only provide the whole context to the partial template, which might limit their use cases. For example, in some scenarios a user might want to invoke the same partial template with different contexts so that it ultimately renders different data. Is there maybe a way to keep the .TargetName
accessible for partial templates even though it is not in the inventory context?
Glad that this feature will solve lots of problems for you 😃. And thank you for testing this out ❤️
This is indeed an issue. But unfortunately Go templates only accept one context parameter. I've come up with a solution which might be sufficient.
I have introduced a new template function context
which essentially allows you to build a map[string]interface{}
on the fly.
This allows you to pass in as many arguments as you like, as long as there is an even amount of arguments
and the keys of the map are strings.
This helps you in solving the TargetName
issue.
{{ template "my_template" context "TargetName" .TargetName "Something" .Inventory.something }}
This will create a map with keys TargetName
and Something
with the respective values.
So in your partial you can then use {{ .TargetName }}
again.
Unfortunately I'm not able to automatically inject the targetname as the value is not known when the template functions are defined.
Is this a solution which will work for you?
Oh that is a nice solution. That would even allow us to provide multiple parts of the Inventory to a template instead of the whole Inventory (even though I'm not sure yet why one would that haha).
Tested and it works. Thanks for the quick fix.
I also tested supplying variables into the template, both "primitives" (like strings you define on the fly) as well as declaring parts of the Inventory as variable and supplying them.
{{ $commander := "cody" }}
{{ $firstElement := (index .Inventory.elements 0 ) }
{{ template "dummy" context "Element $firstElement "Commander" $commander }}
With a template like this:
{{ define "dumy" }}
{{ . }}
{{ end }}
... you are then able to see all of those contexts getting supplied correctly (even though they get rearranged alphabetically which threw me off at first).
However, you are not actually able to access .Element
or .Commander
. When supplying contexts to a template like this, you seemingly can only access conecxts where their name is the name of a field that is also available to the template that invokes the partial template:
executing "dummy" at <.Commander>: can't evaluate field Commander in type struct { Inventory interface {}; TargetName string }
Thanks for repeatedly challenging the implementation 👍🏼 Unfortunately I'm not able to reproduce your error.
Given this target
---
target:
skipper:
use:
- network # not used
elements:
- this:
is:
a:
nested: map
- welcome: home
This template:
# main template
{{- $commander := "cody" }}
{{- $firstElement := (index .Inventory.elements 0 ) }}
{{ template "with_data" context "TargetName" .TargetName "Network" .Inventory.network "Commander" $commander "Elements" $firstElement }}
And this partial
{{ define "with_data" }}
# {{ .TargetName }} partial
{{ . }}
{{ .Elements.this.is.a.nested }}
{{ .Commander }}
{{ end }}
Yields the following rendered main.md
file:
# main template
# example partial
map[Commander:cody Elements:this:
is:
a:
nested: map
Network:foo: bar
TargetName:example]
map
cody
Did I miss anything?
I can't reproduce it either. It's possible I just messed up the local skipper vendoring. Everything works now. 🚀
Partial Templates allow the user to deduplicate template code by defining a template at a single place and reuse it within different templates.
{{ define "unique_tpl_name" }}
block, it should be available to use by all other templates by using the{{ template "unique_tpl_name" . }}
syntax