Reputation: 493
I am trying to use text/template
to generate ini-like configurations on the fly, where the original data is provided in yaml format.
I want the resulting output to be different depending on where the request is coming from.
Please consider the following broken code:
package main
import (
"fmt"
"gopkg.in/yaml.v3"
"os"
"text/template"
)
var yamlData = `
# comment
---
States:
- StateName: California
DateEstablished: September 9, 1850
Cities:
- CityName: Los Angeles
Population: 4 Million
- CityName: Santa Barbara
Population: 92 Thousand
- CityName: San Jose
Population: 1 Million
- StateName: Washington
DateEstablished: November 11, 1889
Cities:
- CityName: Seattle
Population: 724 Thousand
Climate: wet
- CityName: Spokane
Population: 217 Thousand
Climate: dry
- CityName: Scappoose
Population: 7
`
const reportTemplate string = `{{ range . }}
# {{ if .CityName == requestingCityName }}
# {{ .CityName }}
[RequestingCity]
Population = {{ .Population }}
{{ if .Climate }}Climate = {{ .Climate }}{{ end }}
{{ end }}
# {{ if .CityName != requestingCityName }}
# {{ .CityName }}
[City]
Population = {{ .Population }}
{{ if .Climate }}Climate = {{ .Climate }}{{ end }}
{{ end }}
{{ end }}
`
type dataStruct struct {
States []struct {
StateName string `yaml:"StateName"`
DateEstablished string `yaml:"DateEstablished"`
Cities []struct {
CityName string `yaml:"CityName"`
Population string `yaml:"Population"`
Climate string `yaml:"Climate,omitempty"`
} `yaml:"Cities"`
} `yaml:"States"`
}
func (d *dataStruct) readData(data []byte) *dataStruct {
yaml.Unmarshal(data, d)
return d
}
func main() {
var report dataStruct
report.readData([]byte(yamlData))
t := template.Must(template.New("new").Parse(reportTemplate))
requestingStateName := "Washington"
requestingCityName := "Seattle"
for i := range report.States {
if report.States[i].StateName == requestingStateName {
x := report.States[i].Cities
fmt.Println(t.Execute(os.Stdout, x))
}
}
}
Most of this code "works" the way i would expect it to, but the part i am having problems with is how to make the template.
I want to be able to change the value for requestingCityName
so that the output will change like so:
if requestingCityName
== "Scappoose"
then the output would be like so:
# Scappoose
[RequestingCity]
Population = 7
# Spokane
[City]
Population = 217 Thousand
Climate = dry
# Seattle
[City]
Population = 724 Thousand
Climate = wet
or if requestingCityName
== "Seattle"
then the output would be like so:
# Seattle
[RequestingCity]
Population = 724 Thousand
Climate = wet
# Spokane
[City]
Population = 217 Thousand
Climate = dry
# Scappoose
[City]
Population = 7
How would make the template so that I can achieve the desired behaviour?
Upvotes: 3
Views: 1313
Reputation: 5348
You can do the if-condition inside the template with eq
, see Compare strings in templates. To negate an if you can write if not <a> eq <b>
.
I assumed you don't care about order. If you care you can sort it beforehand so at the top is the requested city and everything else below. Else you have to split the request-city-printing up and output outside of the range the requested, then filter out the requested once you range over all cities.
The most clean is probably to go with a separate attribute inside the data struct where the requesting city is held and filter that one out of cities beforehand.
Oh and if you haven't added the requested city to the data struct, then do that beforehand. It's probably easiest to separate by embedding the yaml data struct by embedding it inside a struct named DataRendering
which also has the requested city attribute.
Upvotes: 1
Reputation: 43919
One simple solution would be to pass a different data object to Template.Execute
. Something like:
type templateData struct {
requestingCityName string
cities []citiesStruct // or whatever you name the struct
}
...
fmt.Println(t.Execute(os.Stdout, templateData{requestingCityName, x}))
This solution would require you to update your template to work with the new context struct (i.e. that the old cities array is now .cities
rather than .
), but it gives you access to .requestingCityName
.
Upvotes: 4