How can I set a variable in each template that I can use in other templates e.g.
{{ set title "Title" }}
in one template then in my layout
<title> {{ title }} </title>
Then when it's rendered
tmpl, _ := template.ParseFiles("layout.html", "home.html")
it will set the title according to whatever was set in home.html instead of having to make a struct for each view page when it isn't really necessary. I hope I made sense, thanks.
Just for clarification:
layout.html:
<!DOCTYPE html>
<html>
<head>
<title>{{ title }} </title>
</head>
<body>
</body>
</html>
home.html:
{{ set Title "Home" . }}
<h1> {{ Title }} Page </h1>
If you want to use the Value in another template you can pipeline it to the dot:
{{with $title := "SomeTitle"}}
{{$title}} <--prints the value on the page
{{template "body" .}}
{{end}}
body template:
{{define "body"}}
<h1>{{.}}</h1> <--prints "SomeTitle" again
{{end}}
As far as i know it is not possible to go upwards in the chain.
So layout.html gets rendered before home.html, so you cant pass a value back.
In your example it would be the best solution to use a struct and pass it from the layout.html to the home.html using the dot:
main.go
package main
import (
"html/template"
"net/http"
)
type WebData struct {
Title string
}
func homeHandler(w http.ResponseWriter, r *http.Request) {
tmpl, _ := template.ParseFiles("layout.html", "home.html")
wd := WebData{
Title: "Home",
}
tmpl.Execute(w, &wd)
}
func pageHandler(w http.ResponseWriter, r *http.Request) {
tmpl, _ := template.ParseFiles("layout.html", "page.html")
wd := WebData{
Title: "Page",
}
tmpl.Execute(w, &wd)
}
func main() {
http.HandleFunc("/home", homeHandler)
http.HandleFunc("/page", pageHandler)
http.ListenAndServe(":8080", nil)
}
layout.html
<!DOCTYPE html>
<html>
<head>
<title>{{.Title}} </title>
</head>
<body>
{{template "body" .}}
</body>
</html>
home.html
{{define "body"}}
<h1>home.html {{.Title}}</h1>
{{end}}
page.html
{{define "body"}}
<h1>page.html {{.Title}}</h1>
{{end}}
Also go has a nice documentation on how to use templates:
http://golang.org/pkg/text/template/
Related
I'm playing with Go in a simple HTTP server:
// var tpl = template.Must(template.New("").Funcs(template.FuncMap{"isRegistered": isRegistered}).ParseGlob("templates/*")) // functions will be added later
var tpl = template.Must(template.ParseGlob("templates/*"))
func contact(w http.ResponseWriter, r *http.Request) {
//// defined templates are: "home.html", "layout", "layout.html", "contact.html", "body"
log.Println("in handler: ", tpl.DefinedTemplates())
err := tpl.ExecuteTemplate(w, "contact.html", nil)
if err != nil {
fmt.Println(err) // no error displayed
}
// fmt.Fprintf((w), "write") - This works fine
}
func main() {
log.Println("Serving on 8888 port")
http.HandleFunc("/contact", contact)
http.ListenAndServe(":8888", nil)
}
{{define "layout"}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{.Title}}</title>
<meta name="description" content="{{.Description}}">
<link rel="canonical" href="{{.Canonical}}" />
</head>
<body>
{{template "body" .}}
</body>
</html>
{{end}}
{{define "body"}}
<h1>Contact us page</h1>
<p>
Your name is...
</p>
{{end}}
The localhost:8888/contact returns OK 200 and empty body.
I used this example: https://stackoverflow.com/a/36643663/2110953
But I need to add template functions in future also:
var tpl = template.Must(template.New("").Funcs(template.FuncMap{"isRegistered": isRegistered}).ParseGlob("templates/*"))
Your contact.html does not "render" anything. It just defines the body template, but does not include it (execute it).
To execute a template (within a template), you may use the {{template}} action. To define and execute a template, you may use the {{block}} action.
Template Actions:
{{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.
{{block "name" pipeline}} T1 {{end}}
A block is shorthand for defining a template
{{define "name"}} T1 {{end}}
and then executing it in place
{{template "name" pipeline}}
The typical use is to define a set of root templates that are
then customized by redefining the block templates within.
If your goal is to have a "fixed" header and footer in all pages, then you have to restructure your templates. Have a header and footer template defined somewhere, and the pages should include them as first and last elements. See How to use a field of struct or variable value as template name?
Update: So I had to just create a header and footer templates:
{{template "header" .}}
<h1>Contact us page</h1>
<p>
Your name is...
</p>
{{template "footer" .}}
{{define "header"}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{.Title}}</title>
<meta name="description" content="{{.Description}}">
<link rel="canonical" href="{{.Canonical}}" />
</head>
<body>
{{end}}
{{define "footer"}}
</body>
</html>
{{end}}
and it worked fine
How do i pass the data to the right template?
I have the following templates and want to parse them
layout.html:
<!DOCTYPE html>
<html>
<head>
...
</head>
<body>
<header>
...
</header>
<main>
{{template "main"}}
</main>
</body>
</html>
list.html:
{{define "main"}}
{{range $index, $element := . }}
<div>
<a href=#>{{ $element.Data1 }}</a>
<p>{{ $element.Data2 }}</p>
<p>{{ $element.Data3 }}</p>
</div>
{{end}}
{{end}}
When i use this in the handler func only the "main" template is executed and i dont get the layout.
t, err := template.ParseFiles(layoutPath, templatePath)
t.ExecuteTemplate(w, "main", Data)
And with this i dont have the Data in the list template and so cant display the list.
t, err := template.ParseFiles(layoutPath, templatePath)
t.ExecuteTemplate(w, Data)
So how do i execute this properly?
From the docs:
{{template "name" pipeline}} The template with the specified name is
executed with dot set to the value of the pipeline.
This means that to pass data from the layout.html template to the list.html template you need to pass the data as the second argument of the template action.
E.g. {{template "main" .}}
import "os"
import "html/template"
...
t, _ := template.ParseFiles("login.html")
t.Execute(os.Stdout, data)
...
login.html:
{{ template "header.html" . }}
<form ....>...</form>
{{ template "footer.html" . }}
no output, no error.
If I remove those two lines of {{ template "..." . }}, I could see the part being outputed.
What's required to make {{ template "..." . }} work or am I misunderstanding html/template completely?
You need to define a name for the file that will contain the other templates and then execute that.
login.tmpl
{{define "login"}}
<!doctype html>
<html lang="en">
..
{{template "header" .}}
</body>
</html>
{{end}}
header.tmpl
{{define "header"}}
whatever
{{end}}
Then you parse both those files
t := template.Must(template.ParseFiles("login.tmpl", "header.tmpl"))
// and then execute the template with the defined name
t.ExecuteTemplate(os.Stdout, "login", data)
I am having a problem controlling whitespace and still formatting html/template templates in a readable fashion. My templates look somthing like this:
layout.tmpl
{{define "layout"}}
<!DOCTYPE html>
<html>
<head>
<title>{{.title}}</title>
</head>
<body>
{{ template "body" . }}
</body>
</html>
{{end}}
body.tmpl
{{define "body"}}
{{ range .items }}
{{.count}} items are made of {{.material}}
{{end}}
{{end}}
code
package main
import (
"os"
"text/template"
)
type View struct {
layout string
body string
}
type Smap map[string]string
func (self View) Render(data map[string]interface{}) {
layout := self.layout + ".tmpl"
body := self.body + ".tmpl"
tmpl := template.Must(template.New("layout").ParseFiles(layout, body))
tmpl.ExecuteTemplate(os.Stdout, "layout", data)
}
func main() {
view := View{ "layout", "body" }
view.Render(map[string]interface{}{
"title": "stock",
"items": []Smap{
Smap{
"count": "2",
"material": "angora",
},
Smap{
"count": "3",
"material": "wool",
},
},
})
}
But that produces (note: there is a line above the doctype):
<!DOCTYPE html>
<html>
<head>
<title>stock</title>
</head>
<body>
2 items are made of angora
3 items are made of wool
</body>
</html>
What I want is:
<!DOCTYPE html>
<html>
<head>
<title>stock</title>
</head>
<body>
2 items are made of angora
3 items are made of wool
</body>
</html>
In other template languages I can say things like
[[- value -]]
and the whitespace before and after the action are stripped, but I don't see anything like that in html/template. Does this really mean I have to make my templates unreadable like the following?
layout.tmpl
{{define "layout"}}<!DOCTYPE html>
<html>
<head>
<title>.title</title>
</head>
<body>
{{ template "body" . }} </body>
</html>
{{end}}
body.tmpl
{{define "body"}}{{ range .items }}{{.count}} items are made of {{.material}}
{{end}}{{end}}
You can use white space controller
{{range .foos -}} // eats trailing whitespace
<tr><td>do something</td></tr>
{{- end}} // eats leading whitespace (\n from previous line)
Whitespace in this case makes no difference in the rendered output at the user's browser, so controlling it makes little sense above perhaps aesthetics.
Put differently, one can have nicely formatted templates (which I would prefer) or partially nicely formatted HTML (no nested indents). Pick one or post process the HTML using any of the existing formatters.
Yes, whitespace and lines are translated literally. If you have a line with just a {{ define }} or anything else that does not produce output, you will have an empty line in the parsed file.
Ideally, because you are using template, you should not need to view or edit the parsed output. For reference, use JSP/JSF and see the ugly output it gives you. View the source of most pages online, they're also ugly.
Good luck!
Given a set of templates like:
layout.tpl
<html>
<head>
<title>Some title</title>
{{template extracss}}
</head>
<body>
<h1>Page title</h1>
{{template content .}}
</body>
</html>
home.tpl
{{define "content"}}
<p>page content goes here</p>
{{end}}
edit.tpl
{{define "content"}}
<form>form content goes here</form>
{{end}}
{{define "extracss"}}<style>body{background:pink}</style>{{end}}
using this to render the template:
func Render(w http.ResponseWriter, tmpname string, data interface{}) {
t, err := template.ParseFiles("views/layout.tpl", "views/"+tmpname+".tpl")
// parse error
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
if err := t.Execute(w, data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
edit.tpl will render correctly as it has 'extracss' defined, home.tpl will not as the template parser rightly says 'no such template "extracss"'.
So what mechanism would I use to allow 'optional' templates to be used?
Any ideas?
An empty define works: {{define "extracss"}}{{end}}. It's maybe not super elegant, but simple to understand.
Note that you don't need to repeat the empty defines. You can put them into your master template and redefine them in the included templates only if needed.
The answer from #thomas is awesome, but I found what he wrote ambiguous and wasted a lot of time trying to translate it into code.
Here's code that works (and I believe is what he's suggesting):
layout.tpl
<html>
<head>
<title>Some title</title>
{{template extracss}}
{{define "extracss"}}{{end}}
</head>
<body>
<h1>Page title</h1>
{{template content .}}
</body>
</html>
home.tpl
{{define "content"}}
<p>page content goes here</p>
{{end}}
edit.tpl
{{define "content"}}
<form>form content goes here</form>
{{end}}
{{define "extracss"}}<style>body{background:pink}</style>{{end}}
using this to render the template:
func Render(w http.ResponseWriter, tmpname string, data interface{}) {
t, err := template.ParseFiles("views/layout.tpl", "views/"+tmpname+".tpl")
// parse error
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
if err := t.Execute(w, data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
Perhaps this wasn't available at the time the question was asked but it seems that you can use block for this purpose:
{{block "name" pipeline}} T1 {{end}}
A block is shorthand for defining a template
{{define "name"}} T1 {{end}}
and then executing it in place
{{template "name" pipeline}}
The typical use is to define a set of root templates that are then customized by redefining the block templates within.
layout.tpl
<html>
<head>
<title>Some title</title>
{{block "extracss" .}}{{end}}
</head>
<body>
<h1>Page title</h1>
{{block "content" .}}{{end}}
</body>
</html>
home.tpl
{{define "content"}}
<p>page content goes here</p>
{{end}}
edit.tpl
{{define "content"}}
<form>form content goes here</form>
{{end}}
{{define "extracss"}}<style>body{background:pink}</style>{{end}}