How to Render multiple templates
Solution 1
If you define all your templates in a template-folder, you can easily parse the whole directory with:
template.Must(template.ParseGlob("YOURDIRECTORY/*"))
For example:
head.html
{{define "header"}}
<head>
<title>Index</title>
</head>
{{end}}
index.html
{{define "indexPage"}}
<html>
{{template "header"}}
<body>
<h1>Index</h1>
</body>
</html>
{{end}}
main.go
package main
import(
"html/template"
)
// compile all templates and cache them
var templates = template.Must(template.ParseGlob("YOURTEMPLATEDIR/*"))
func main(){
...
}
func IndexHandler(w http.ResponseWriter, r *http.Request) {
// you access the cached templates with the defined name, not the filename
err := templates.ExecuteTemplate(w, "indexPage", nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
You execute your indexPage-Template with templates.ExecuteTemplate(w, "indexPage", nil)
Solution 2
You can easily add more .html files by just adding them as arguments:
var tmpl = template.Must(template.ParseFiles(
"templates/base.html",
"templates/first.html",
"templates/second.html",
))
This works fine as long as first and second don't define the same template.
However, the template package does not allow dynamic invocation of templates, using a pipeline value for the template name. So, if you are trying to do something similar to my example below, then it will not work.
Some workarounds exists, and there is a discussion about it on Go-nuts. But it seems the template package is designed that you should have one *Template
object per page.
Broken example of attempted dynamic template invocation:
Error: unexpected ".Content" in template invocation
package main
import (
"log"
"os"
"text/template"
)
const base= `<!DOCTYPE html>
<html>
<head><title>Title</title></head>
<body>
{{template .Content}}
</body>
</html>`
const first = `{{define "first"}}This is the first page{{end}}`
const second = `{{define "second"}}And here is the second{{end}}`
type View struct {
Content string
}
func main() {
var view = &View{ "first" } // Here we try to set which page to view as content
t := template.Must(template.New("base").Parse(base))
t = template.Must(t.Parse(first))
t = template.Must(t.Parse(second))
err := t.Execute(os.Stdout, view)
if err != nil {
log.Println("executing template:", err)
}
}
Solution 3
If you want to parse multiple directories a long with specific files, here's a code snippet for it:
//parse a pattern in a specific directory
allTemplates := template.Must(template.ParseGlob("Directory/*"))
//add another directory for parsing
allTemplates = template.Must(allTemplates.ParseGlob("Another_Directory/*.tmpl"))
//add specific file name
allTemplates = template.Must(allTemplates.ParseFiles("path/to/file.tmpl"))
func main() {
...
}
func FileHandler(w http.ResponseWriter, r *http.Request) {
// access cached template by file name (in case you didn't define its name)
err := allTemplates.ExecuteTemplate(w, "file.tmpl", nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func NameHandler(w http.ResponseWriter, r *http.Request) {
// access cached template by handler name
err := allTemplates.ExecuteTemplate(w, "handlerName", nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
a quick shortcut:
allTemplates := template.Must(
template.Must(
template.Must(
template.ParseGlob("directory/*.tmpl")).
ParseGlob("another_directory/*.tmpl")).
ParseFiles("path/to/file.tmpl")),
)
file.tmpl
can be like the following:
<html>
This is a template file
</html>
name.tmpl
should be looking like this
{{define "handlerName" }}
<p>this is a handler</p>
{{end}}
Solution 4
part 3 of this tutorial is useful:
http://golangtutorials.blogspot.co.nz/2011/11/go-templates-part-3-template-sets.html
Sample from the tutorial:
Full template file - t1.tmpl
{{define "t_ab"}}a b{{template "t_cd"}}e f {{end}}
The file above will be parsed in as a template named "t_ab". It has, within it, "a b /missing/ e f", but is missing a couple of letters in the alphabet. For that it intends to include another template called "t_cd" (which should be in the same set).
Full template file - t2.tmpl
{{define "t_cd"}} c d {{end}}
The file above will be parsed in as a template called "t_cd".
Full program
package main
import (
"text/template"
"os"
"fmt"
)
func main() {
fmt.Println("Load a set of templates with {{define}} clauses and execute:")
s1, _ := template.ParseFiles("t1.tmpl", "t2.tmpl") //create a set of templates from many files.
//Note that t1.tmpl is the file with contents "{{define "t_ab"}}a b{{template "t_cd"}}e f {{end}}"
//Note that t2.tmpl is the file with contents "{{define "t_cd"}} c d {{end}}"
s1.ExecuteTemplate(os.Stdout, "t_cd", nil) //just printing of c d
fmt.Println()
s1.ExecuteTemplate(os.Stdout, "t_ab", nil) //execute t_ab which will include t_cd
fmt.Println()
s1.Execute(os.Stdout, nil) //since templates in this data structure are named, there is no default template and so it prints nothing
}
fmt.Fprint
Updated on December 06, 2020Comments
-
fmt.Fprint over 3 years
One base template is created. With that rendered first.html one more template.
eg. : var tmpl = template.Must(template.ParseFiles( "templates/base.html", "templates/first.html", ))
But I also want to add more .html files to render. Any reference?
-
fmt.Fprint almost 11 yearsANisus : If I click event trigger on button of first page then it should render the other page mentioned ... There are Redirect and renderTemplate functions but they are also not working. For base.html and first.html I am able to render the page. But for third html page it is not working
-
ANisus almost 11 years@fmt.Fprint I think what you need is to get some structure for your templates and handlers. Kyle Finley has a good suggestion for this: (stackoverflow.com/a/9587616/694331)
-
fmt.Fprint almost 11 years:main.go : var templates template.Template templates = template.Must(template.ParseGlob("templates/.tmpl")) func init(){http.HandleFunc("/", handler)} func handler(w http.ResponseWriter, r *http.Request){templates.ExecuteTemplate(w, "indexPage", nil)} This example is giving Error "expected declaration, found 'IDENT' templates"
-
fmt.Fprint almost 11 yearsThis link is already tried. base.html and first.html is fine. But problem with third and fourth ones
-
ioboi almost 11 yearsYour declaration is false, it should be: var templates := template.Must(template.ParseGlob("templates/.tmpl/.*")) I assume that all your templates are in the templates/.tmpl/.* directory
-
eduncan911 almost 10 yearsI updated this answer to correct the typeos and compile errors. +1 as this help me myself to correctly define multiple templates. It isnt clear at first as most of us coming from .net and other languages with layout and templates that you do NOT create one single master and a single content area but instead define shared header and footers and sidebars. This is actually nice because you can control rather to show the sidebar or not on a per template process.
-
swill about 9 yearsThis should be the accepted answer. This answer is MUCH clearer than the documentation. It shows how to use the global
templates
variable so you load all your templates on server start instead of in your actual handler (which would be loaded on each page load). It also shows how to define a template by name and then call it withtemplates.ExecuteTemplate
, which is super helpful. By far the best documentation I have seen on this topic... -
wuliwong over 8 yearsI'm quite new to golang but it seems the issue boils down to the fact that you cannot pass a variable of type string to the
template
method but only a string literal? I'm also not a CS guy, is there an obvious reason for the Go developers to make this choice? I guess this is actually in the documentation here:{{template "name"}} The template with the specified name is executed with nil data.
I just didn't pick up that they were writing "name" to imply a string literal instead of writingname string
. -
FisNaN about 5 yearsHave you test it?
syntax error: non-declaration statement outside function body
... -
Muhammad Soliman about 5 years@FisNan have you tried to use
var allTemplates = ...
instead. if it doesn't help, you still can put all the code insidemain()
func , the answer gives you idea how to do it in general .. hope my comment would help you though, thanks -
FisNaN about 5 yearsI worked out with a global null pointer
var allTemplates *template.Template
and then put alltemplate.Must(...)
insidemain
function. Thanks for the response though!