I know rendering a partial template with additional parameters is possible in Ruby, how can I do it in Go?
I have a partial template _partial1.tmpl:
<div>
text1
{{if foo}}
text2
{{end}}
</div>
using it from the parent template parent.tmpl:
<div>
{{ template "partial1", }} // how do I pass foo param here??
</div>
How do I pass the parameter foo to the partial?
The documentation states that the template directive has two forms:
{{template "name"}}
The template with the specified name is executed
with nil data.
{{template "name" pipeline}}
The template with the specified name is
executed with dot set to the value of the pipeline.
The latter accepts a pipeline statement which's value is then set to the dot value in the executed template. So calling
{{template "partial1" "string1"}}
will set {{.}} to "string1" in the partial1 template. So while there is no way to set the name foo in the partial, you can pass parameters and they will appear in .. Example:
template.html
<div>
{{ template "partial1.html" "muh"}} // how do I pass foo param here??
</div>
partial1.html
{{if eq . "muh"}}
blep
{{else}}
moep
{{end}}
main.go
import (
"html/template"
"fmt"
"os"
)
func main() {
t,err := template.ParseFiles("template.html", "partial1.html")
if err != nil { panic(err) }
fmt.Println(t.Execute(os.Stdout, nil))
}
Running this program will print the template's contents with blep from the partial. Changing the passed value will change this behaviour.
You can also assign variables, so assigning . to foo is possible in the partial:
{{ $foo := . }}
Related
This question already has an answer here:
Variable in template's included template
(1 answer)
Closed 6 years ago.
In golang, I am working with three files: index.html, nav.html and main.go
nav.html contains the following:
{{ define "nav" }}
<nav class="nav-container">
<h1>{{ .path }}</h1>
</nav>
{{ end }}
index.html contains the following:
{{ define "index" }}
{{ template "nav" }} <!-- Includes the nav.html file -->
<h1>Welcome to my website. You are visiting {{ .path }}.</h1>
{{ end }}
I am using Golang's template package along with Martini which is not too important in this case.
My main.go file contains:
package main
import (
"net/http"
"github.com/go-martini/martini"
"github.com/martini-contrib/render"
)
func main() {
m := martiniSetup()
m.Get("/", func(res http.ResponseWriter, req *http.Request, ren render.Render, params martini.Params) {
parse := make(map[string]interface{})
parse["path"] = req.URL.Path
ren.HTML(http.StatusOK, "index", parse)
})
m.Run()
}
My problem:
The .path variable being parsed into the index template is only accessable by the index template itself.
I include the nav template using {{ template "nav" }} inside index.html. The issue is, nav.html cannot access the .path variable. It is only accessable by the index template.
Is there any way to make the .path variable accessable to all included template files, in my case index.html and nav.html?
You can pass the data to the nested template as an argument like this: {{ template "nav" . }}
Now the dot will be accessible within the define "nav" block.
I'm trying to use a variable declared outside a Go template range loop to see if the previous post occurred on the same day as the current post. Here's a simplified example.
Where .Posts is an array of post structs that each have a .Content and a .Date.
{{ $prevDate := "" }}
{{ range $post := .Posts }}
{{ if ne $prevDate $post.Date }}
<div class="post-date">Posts dated: {{ $post.Date }}</div>
{{ end }}
<div class="post-content">{{ $post.Content }}</div>
{{ $prevDate := $post.Date }}
{{ end }}
The problem is that $prevDate seems to be reset to "" at the start of each iteration of the loop.
Can anyone help me understand why the value of $prevDate is reset on each iteration and perhaps suggest a way to accomplish what I'm trying to do here?
Note: Go 1.11 will support modifying template variables via assignment. This will be valid code:
{{ $v := "init" }}
{{ if true }}
{{ $v = "changed" }}
{{ end }}
v: {{ $v }} {{/* "changed" */}}
Original answer pre-dating Go 1.11 follows:
Variables are not reset. Basically what happens is that you redeclare the $prevDate variable inside the loop. But it is only in scope after the redeclaration and before the closing {{end}} tag of the {{range}}. So when the next iteraiton of the loop comes, you only see the "outer" variable which you haven't changed (because you created a new).
You can't change the values of the template variables you create.
What you can do is for example use the following range form:
{{ range $index, $post := .Posts }}
And...
Solution #1: with a registered Function
And you can register a function for the template (see template.Funcs()) to which you can pass the $index and it would return the date field of the previous element (at $index -1).
It would look something like this:
func PrevDate(i int) string {
if i == 0 {
return ""
}
return posts[i-1].Date
}
// Registering it:
var yourTempl = template.Must(template.New("").
Funcs(map[string]interface{}{"PrevDate": PrevDate}).
Parse(yourStringTemplate))
And from your template you can call it like:
{{range $index, $post := .Posts}}
{{$prevDate := PrevDate $index}}
{{end}}
Solution #2: with a Method of Posts
This solution is analog but is even simpler: add a method to your Posts and you can call it directly. No need to register a function.
For example:
type Post struct {
// Your Post type
Date string
}
type Posts []Post
func (p *Posts) PrevDate(i int) string {
if i == 0 {
return ""
}
return (*p)[i-1].Date
}
And from your template you can call it like:
{{range $index, $post := .Posts}}
{{$prevDate := $.Posts.PrevDate $index}}
{{end}}
Go templates are not designed to support complex logic. There's the Go programming language for that. Templates have limitations as a consequence of this philosophy. One limitation is that template variables cannot be changed.
One way to handle this limitation is to structure the data in Go to match the structure of output. Create a type to hold posts for a date and render a slice of these types. The template simply ranges through PostsForDate and Posts.
type PostsForDate struct {
Date time.Time
Posts []*Post
}
var Dates []PostsForDate
{{range .Dates}}
<div class="post-date">Posts dated: {{.Date}}</div>
{{range .Posts}}
<div class="post-content">{{.Content}}</div>
{{end}}
{{end}}
A simpler option (that goes against the design philosophy to some degree) is to create a type in Go to record a current value and report changes to that value.
type change struct {
current interface{}
}
func (c *change) Changed(next interface{}) bool {
result := c.current != next
c.current = next
return result
}
func newChange() *change {
return &change{&struct{ int }{}} // initial value ensures that first change is fired.
}
and hook it into a template using a template function:
t := template.Must(template.New("").Funcs(template.FuncMap{"change": newChange}).Parse(` some template `))
Use it in a template like this:
{{ $i := change }}
{{ range $post := .Posts }}
{{ $i.Change $post.Date }}
<div class="post-date">Posts dated: {{ $post.Date }}</div>
{{ end }}
<div class="post-content">{{ $post.Content }}</div>
{{ end }}
playground example
If the post Date field is a time.Time and the posts have different times within a day, then the above does not work as desired. A workaround for this is to check for changes in the rendered date (for example $post.Date.Format "2006-01-02"). Add the following method to simplify this:
func (c *change) ChangedValue(next interface{}) interface{} {
if c.current != next {
c.current = next
return next
}
return nil
}
Use it like this:
{{ $i := change }}
{{ range $post := .Posts }}
{{with $i.ChangedValue ($post.Date.Format "2006-01-02")}}
<div class="post-date">Posts dated: {{.}}</div>
{{ end }}
<div class="post-content">{{ $post.Content }}</div>
{{ end }}
This only works when the values are guaranteed to be considered true by the template package.
This solution does not require parsing the template on every use (as in solution #1 in the other answer) and it applies to arbitrary slice types (unlike both solutions in the other answer).
Is it possible to use template layouts in Revel. For example have a root.html which contains a {{define "main"}}{{end}} tag. Then call ExecuteTemplate(out, "main", nil) http://golang.org/pkg/html/template/#Template.ExecuteTemplate
Something like c.RenderLayout()
You can define the do you want into conf/routes file.
Then you can call you action with the name you want:
package controllers
import (
"github.com/revel/revel"
)
func (c Name) root() revel.Result {
c.Render()
}
views/Name/root.html
Other way can be this:
views/Controler/NameAction.html
{{template "root.html" .}}
views/root.html
<p>bla, bla</p>
I have been trying to use nested templates in Go, however the Examples or help documents are not helping me, and examples in 3 other blogs are not what I'm looking for (one is close and maybe the only way but I want to make sure)
OK, so my code is for App Engine, in here I will be doing an urlfetch to a server, and then I want to show some results, like the Response, headers, and body.
const statusTemplate = `
{{define "RT"}}
Status - {{.Status}}
Proto - {{.Proto}}
{{end}}
{{define "HT"}}
{{range $key, $val := .}}
{{$key}} - {{$val}}
{{end}}
{{end}}
{{define "BT"}}
{{.}}
{{end}}
{{define "MT"}}
<html>
<body>
<pre>
-- Response --
{{template "RT"}}
-- Header --
{{template "HT"}}
-- Body --
{{template "BT"}}
</pre>
</body>
</html>
{{end}}
{{template "MT"}}`
func showStatus(w http.ResponseWriterm r *http.Request) {
/*
* code to get:
* resp as http.Response
* header as a map with the header values
* body as an string wit the contents
*/
t := template.Must(template.New("status").Parse(statusTemplate)
t.ExecuteTemplate(w, "RT", resp)
t.ExecuteTemplate(w, "HT", header)
t.ExecuteTemplate(w, "BT", body)
t.Execute(w, nil)
}
My code actually outputs the RT, HT, and BT templates with the correct values, and then it outputs the MT template empty (MT stands for main template).
So... How can I use nested forms from a string variable so that an example as the above works?
I think the approach you try with nested templates is wrong. If you want . to be defined inside a nested template, you have to supply an argument to the call to the nested template, just as you do with the ExecuteTemplate function:
{{define "RT"}}
Status - {{.Status}}
Proto - {{.Proto}}
{{end}}
{{define "HT"}}
{{range $key, $val := .}}
{{$key}} - {{$val}}
{{end}}
{{end}}
{{define "BT"}}
{{.}}
{{end}}
{{define "MT"}}
<html>
<body>
<pre>
-- Response --
{{template "RT" .Resp}}
-- Header --
{{template "HT" .Header}}
-- Body --
{{template "BT" .Body}}
</pre>
</body>
</html>
{{end}}
{{template "MT"}}
The important part you seem to miss is that templates do not encapsulate state. When you execute a template, the engine evaluates the template for the given argument and then writes out the generated text. It does not save the argument or anything that was generated for future invocations.
Documentation
Relevant part of the documentation:
Actions
Here is the list of actions. "Arguments" and "pipelines" are
evaluations of data, defined in detail below.
...
{{template "name"}}
The template with the specified name is executed with nil data.
{{template "name" pipeline}}
The template with the specified name is executed with dot set
to the value of the pipeline.
...
I hope this clears up things.
have a look at this tutorial:
http://javatogo.blogspot.com
there it is explained how to use nested templates.
When I'm calling a Go template function to output HTML, it displays ZgotmplZ.
Sample code:
http://play.golang.org/p/tfuJa_pFkm
package main
import (
"html/template"
"os"
)
func main() {
funcMap := template.FuncMap{
"printSelected": func(s string) string {
if s == "test" {
return `selected="selected"`
}
return ""
},
"safe": func(s string) template.HTML {
return template.HTML(s)
},
}
template.Must(template.New("Template").Funcs(funcMap).Parse(`
<option {{ printSelected "test" }} {{ printSelected "test" | safe }} >test</option>
`)).Execute(os.Stdout, nil)
}
Output:
<option ZgotmplZ ZgotmplZ >test</option>
"ZgotmplZ" is a special value that indicates that unsafe content reached a
CSS or URL context at runtime. The output of the example will be:
<img src="#ZgotmplZ">
You can add a safe and attr function to the template funcMap:
package main
import (
"html/template"
"os"
)
func main() {
funcMap := template.FuncMap{
"attr":func(s string) template.HTMLAttr{
return template.HTMLAttr(s)
},
"safe": func(s string) template.HTML {
return template.HTML(s)
},
}
template.Must(template.New("Template").Funcs(funcMap).Parse(`
<option {{ .attr |attr }} >test</option>
{{.html|safe}}
`)).Execute(os.Stdout, map[string]string{"attr":`selected="selected"`,"html":`<option selected="selected">option</option>`})
}
The output will look like:
<option selected="selected" >test</option>
<option selected="selected">option</option>
You may want to define some other functions which can convert string to template.CSS, template.JS, template.JSStr, template.URL etc.
I had similar problem with <img src="{{myfunction}}"> where myfunction return encoded image.
Finally I solved it when instead of string function return template.URL(mystring).
You are trying to output HTML in a place where template/html thinks is unsafe (for example, inside an HTML element, like this:
<option {{ printSelected }}>
I cannot find any way to convince it it is safe (including returning template.HTML instead of string); the only alternative I have found is to rewrite the template, in this example use a bool output instead:
<option {{ if printSelected }}selected{{ end }}>
package main
import (
"html/template"
"os"
)
type T struct {
HTML template.HTML
ATTR template.HTMLAttr
URL template.URL
JS template.JS
CSS template.CSS
}
func main() {
data := T{
HTML: `<div>test div</div>`,
ATTR: `selected="selected"`,
URL: `https://upload.wikimedia.org/wikipedia/commons/5/53/Google_%22G%22_Logo.svg`,
CSS: `font-size: 15px`,
JS: `console.log("hello world")`,
}
template.Must(template.New("Template").Parse(`
{{.HTML}}
<option {{.ATTR}} style="{{.CSS}}">test</option>
<script>{{.JS}}</script>
<img src="{{.URL}}">
`)).Execute(os.Stdout, data)
}
output
<div>test div</div>
<option selected="selected" style="font-size: 15px">test</option>
<script>console.log("hello world")</script>
<img src="https://upload.wikimedia.org/wikipedia/commons/5/53/Google_%22G%22_Logo.svg">
playground Example
easiest way:
import "html/template"
yourhref = template.URL(yourhref)
You should wrap the string in an HTMLAttr, which was designed for text that gets injected in between angle brackets. Per the documentation:
https://golang.org/pkg/html/template/#HTMLAttr
HTMLAttr encapsulates an HTML attribute from a trusted source, for example, dir="ltr".
Use of this type presents a security risk: the encapsulated content should come from a trusted source, as it will be included verbatim in the template output.
type HTMLAttr string
I was trying to insert an image from frontmatter into my template but kept getting the same error. I solved it thus:
{{ if isset .Params "image" }}
{{ $myVar := print .Params.image }}
<img src="{{ $myVar }}">
{{ end }}
Notice that I first save the .Params.image as a variable, and then insert it as my src.