Im trying to create a handler which then will compile 2 templates:
template.html which serves layout purposes and the actual page: config.html.
this code builds the page, but no data is passed:
func config(w http.ResponseWriter, r *http.Request) {
fpTemplate := filepath.Join("static", "template.html")
fpPage := filepath.Join("static", "config.html")
tmpl, err := template.ParseFiles(fpPage, fpTemplate)
if err != nil {
log.Println("webserver.config: " + err.Error())
}
vd := ViewData{&Settings}
err = tmpl.ExecuteTemplate(w, "template.html", vd)
if err != nil {
log.Println("webserver.config: " + err.Error())
}
}
and config.html like this:
{{define "title"}}
Config
{{end}}
{{define "body"}}
<p class="text-break">
{{ .}}
</p>
{{end}}
, when I run this code:
func config(w http.ResponseWriter, r *http.Request) {
fpTemplate := filepath.Join("static", "template.html")
fpPage := filepath.Join("static", "config.html")
//tmpl, err := template.ParseFiles(fpPage, fpTemplate)
tmpl, err := template.New("config.html").ParseFiles(fpPage, fpTemplate)
if err != nil {
log.Println("webserver.config: " + err.Error())
}
vd := ViewData{&Settings}
err = tmpl.ExecuteTemplate(w, tmpl.Name(), vd)
fmt.Println(err)
//err = tmpl.ExecuteTemplate(w, "template.html", vd)
if err != nil {
log.Println("webserver.config: " + err.Error())
}
}
I get error: template: no template "config.html" associated with template "config.html" and blank black page.
What im I missing here ?
Appreciated any help!
When you pass "vd" to ExecuteTemplate in first code, the data pass to main template and you must pass the data into "body" template when you called it on "template.html" like as:
{{ template "body" . }}
Related
Trying to move my golang html templates from files to using embed
Works fine:
func loadTemplates() multitemplate.Render {
r := multitemplate.New()
layouts, err := filepath.Glob("templates/layouts/*.tmpl")
if err != nil {
panic(err.Error())
}
includes, err := filepath.Glob("templates/includes/*.tmpl")
if err != nil {
panic(err.Error())
}
// Generate our templates map from our layouts/ and includes/ directories
for _, layout := range layouts {
files := append(includes, layout)
r.Add(filepath.Base(layout), template.Must(template.ParseFiles(files...)))
log.Println(filepath.Base(layout) + ": " + files[0])
}
return r
}
Very similar code returns blank page, no errors:
//go:embed templates/*
var f embed.FS
func loadTemplates() multitemplate.Render {
r := multitemplate.New()
// Generate our templates map from our layouts/ and includes/ directories
layouts, err := embed.FS.ReadDir(f, "templates/layouts")
if err != nil {
panic(err.Error())
}
for _, layout := range layouts {
embeddedTemplate, err := template.ParseFS(f, "templates/layouts/"+layout.Name(), "templates/includes/base.tmpl")
if err != nil {
log.Println(err)
}
r.Add(layout.Name(), embeddedTemplate)
log.Println(layout.Name() + " loaded")
}
return r
}
I confirmed in the debugger that all templates contain no errors and their respective content. Other embedded files such as static assets work fine and get served ok. Even other templates loaded from a database work fine. Just those from embed end up blank.
Any hints what's happening here?
Thanks!
Edit: Full example:
main.go
package main
import (
"embed"
"html/template"
"log"
"path/filepath"
"github.com/gin-contrib/multitemplate"
"github.com/gin-gonic/gin"
)
//go:embed templates/*
var f embed.FS
func main() {
router := gin.Default()
router.HTMLRender = loadTemplates()
router.GET("/embed", HomeHandlerEmbed(router))
router.GET("/file", HomeHandlerFile(router))
router.Run(":8080")
}
func loadTemplates() multitemplate.Render {
r := multitemplate.New()
//load same template from embed FS
embeddedTemplate, err := template.ParseFS(f, "templates/layouts/home.tmpl", "templates/includes/base.tmpl")
if err != nil {
log.Println(err)
}
r.Add("homeEmbed.tmpl", embeddedTemplate)
log.Println("homeEmbed.tmpl" + " loaded from embed FS")
// load same template from real file system
layoutsFile, err := filepath.Glob("templates/layouts/*.tmpl")
if err != nil {
panic(err.Error())
}
includes, err := filepath.Glob("templates/includes/*.tmpl")
if err != nil {
panic(err.Error())
}
for _, layout := range layoutsFile {
files := append(includes, layout)
r.Add(filepath.Base(layout), template.Must(template.ParseFiles(files...)))
log.Println(filepath.Base(layout) + ": " + files[0])
}
return r
}
func HomeHandlerEmbed(r *gin.Engine) gin.HandlerFunc {
return gin.HandlerFunc(func(c *gin.Context) {
c.HTML(200, "homeEmbed.tmpl", nil)
})
}
func HomeHandlerFile(r *gin.Engine) gin.HandlerFunc {
return gin.HandlerFunc(func(c *gin.Context) {
c.HTML(200, "home.tmpl", nil)
})
}
templates/includes/base.tmpl
<!DOCTYPE html>
<html>
<head>
{{template "head" .}}
</head>
<body>
{{template "body" .}}
</body>
</html>
templates/layouts/home.tmpl
{{define "head"}}<title>Test</title>{{end}}
{{define "body"}}
Body
{{end}}
/file works fine, /embed comes up blank
In function loadTemplates() just fix this line:
embeddedTemplate, err := template.ParseFS(f, "templates/includes/base.tmpl", "templates/layouts/home.tmpl")
In your example patterns will be presented in this sequence:
first: "templates/layouts/home.tmpl"
second: "templates/includes/base.tmpl"
But if I understood correctly, the sequence of patterns is important for the function template.ParseFS, because base.tmpl will be included in all you templates.
The function template.ParseFS reads the templates in the process and tries to generate them.
I have problem with rendering my layout page in other html pages.
It gives me this error:
home.gohtml:1:11: executing "home.gohtml" at <{{template "base" .}}>: template "base" not defined
But when I do it without using my layout page and just a simple html rendering it works fine.
also when I print my pages in the code it's getting all of my html pages and rendering them.
I have this directory:
.
templates
├── home.gohtml
└── partials
└── base.layout.gohtml
And here is my base.layout.gohtml:
{{define "base"}}
<html>
<head>
{{block "css" .}}{{end}}
</head>
<body>
{{block "content" .}}{{end}}
{{block "js" .}}{{end}}
</body>
</html>
{{end}}
And it's my home.gohtml:
{{template "base" .}}
{{define "content"}}
<p>hey</p>
{{end}}
And it is template.go:
var functions = template.FuncMap{}
func CreateTemplateCache() (map[string]*template.Template, error) {
templateCache := map[string]*template.Template{}
var pages []string
err := filepath.Walk("./templates", func(path string, info fs.FileInfo, err error) error {
if !info.IsDir() {
pages = append(pages, path)
}
return err
})
if err != nil {
return templateCache, err
}
for _, page := range pages {
name := filepath.Base(page)
templateSet, err := template.New(name).Funcs(functions).ParseFiles(page)
if err != nil {
return templateCache, err
}
templateCache[name] = templateSet
}
return templateCache, nil
}
func RenderTemplate(w http.ResponseWriter, templateName string) {
templateCache, err := CreateTemplateCache()
if err != nil {
log.Fatal(err)
}
template, ok := templateCache[templateName]
if !ok {
log.Fatal("Something went wrong!")
}
buf := new(bytes.Buffer)
err = template.Execute(buf, nil)
if err != nil {
log.Fatal(err)
}
_, err = buf.WriteTo(w)
if err != nil {
log.Fatal(err)
}
}
Please help me if you can I'm new to go and stuck at this problem.
I have a following piece of code in which i am trying to send an email using gopkg.in/gomail.v2. I am perfectly able to send the email when the email template is placed in the root directory of the project like this
./
main.go
template.html
// Info defines
type Info struct {
Age int
Name string
}
func (i Info) sendMail() {
fp := filepath.Join("template.html")
t := template.New(fp)
var err error
t, err = t.ParseFiles(fp)
if err != nil {
log.Println(err)
}
var tpl bytes.Buffer
if err := t.Execute(&tpl, i); err != nil {
log.Println(err)
}
result := tpl.String()
// ... email sending logic
}
func main() {
info := &Info{
Name: "name 1",
Age: 20,
}
info.sendMail()
}
but when i change the template directory to emails/template.html and change the filepath to
fp := filepath.Join("emails", "template.html")
then I get the error from t.Execute()
template: "emails/template.html" is an incomplete or empty template
I have also tried
fp, _ := filepath.Abs("emails/template.html")
but got error
template: "/mnt/data/go/test/emails/template.html" is an incomplete or empty template
the path mentioned is correct though.
I changed
if err := t.Execute(&tpl, i); err != nil {
log.Println(err)
}
to
if err := t2.ExecuteTemplate(&tpl, "template.html", i); err != nil {
log.Println(err)
}
and it worked
If I want to use t.Execute(&tpl, i) instead, then I have to specify the templates name as filename while creating the template
t := template.New("template.html")
I'm trying to make an Handler to update one row each time getting data from a submitt button,
here is my code:
func RowHandler(res http.ResponseWriter, req *http.Request) {
if req.Method != "POST" {
http.ServeFile(res, req, "homepage.html")
return
}
Person_id := req.FormValue("Person_id")
stmt, err := db.Prepare("update Cityes set Status='right' where Person_id=?")
if err != nil {
log.Print("error ", err)
}
_, err = stmt.Exec(&Person_id)
t, err := template.ParseFiles("city_update.html") //hier i just want to show a text in html Page
if err != nil {
log.Fatal(err)
}
err = t.Execute(res, "/city_update")
}
Here instead of following
err = t.Execute(res, "/city_update")
pass data to be used to fill your template as send arguement to Execute not string. link to doc
For example .
err = t.Execute(res,struct{ID string}{Person_id})
I have three templates like this:
base.html:
<h1>Base.html rendered here</h1>
{{template "content" .}}
view.html:
{{define "content"}}
...
{{end}}
edit.html:
{{define "content"}}
...
{{end}}
I store them in folder "templates".
What i want is to dynamically change template which will be rendered in {{template "content" .}} place, without parsing every time. So what i DO NOT want is this :
func main() {
http.HandleFunc("/edit", handlerEdit)
http.HandleFunc("/view", handlerView)
http.ListenAndServe(":8080", nil)
}
func handlerView(w http.ResponseWriter, req *http.Request) {
renderTemplate(w, req, "view")
}
func handlerEdit(w http.ResponseWriter, req *http.Request) {
renderTemplate(w, req, "edit")
}
func renderTemplate(w http.ResponseWriter, req *http.Request, tmpl string) {
templates, err := template.ParseFiles("templates/base.html", "templates/"+tmpl+".html")
if err != nil {
fmt.Println("Something goes wrong ", err)
return
}
someData := &Page{Title: "QWE", Body: []byte("sample body")}
templates.Execute(w, someData)
}
I was looking at the template.ParseGlobe(), in order to do something like this
var templates = template.Must(template.ParseGlob("templates/*.html"))
... //and then somthing like this:
err := templates.ExecuteTemplate(w, tmpl+".html", p)
But ExecuteTamplate() recieves only one string as template's name. How in this case i can render two and more templates?
Instead of writing directly to the http.ResponseWriter on your call to ExecuteTemplate, write to a byte buffer and send that through the call to the next template by prepping it with a template.HTML call.
var b bytes.Buffer
var templates = template.Must(template.ParseGlob("templates/*.html"))
err := templates.ExecuteTemplate(b, templ_1, p)
if err != nil { //handle err }
err := templates.ExecuteTemplate(w, templ_2, template.HTML(b.String()))
if err != nil { //handle err }
If you're going to use an unknown number of templates, you can capture the intermediate step with a string:
var strtmp string
err := templates.ExecuteTemplate(b, templ_1, p)
if err != nil { //handle err }
strtemp = b.String() //store the output
b.Reset() //prep buffer for next template's output
err := templates.ExecuteTemplate(b, templ_2, template.HTML(strtmp))
if err != nil { //handle err }
//... until all templates are applied
b.WriteTo(w) //Send the final output to the ResponseWriter
EDIT: As #Zhuharev pointed out, if the composition of the view and edit templates are fixed, they can both reference base rather than base trying to provide a reference to either view or edit:
{{define "viewContent"}}
{{template "templates/base.html" .}}
...Current view.html template...
{{end}}
{{define "editContent"}}
{{template "templates/base.html" .}}
...Current edit.html template...
{{end}}