Change contents of a nested template?


(Sibert) #1

I have ONE layout that I want to populate with MANY other sub pages. I am also trying to change this content dynamically.

The code works as expected but I cannot change the content. The content remains the same. Seems to fetch first match. no matter what the passing template is. Does not work even with hardcoded file names.

Main layout

{{define "layout"}}
    <html>
    <body>
	    {{ template "content" }}
    </body>
    </html>
{{end}}

Sub template 1 - index.gohtml

{{ define "content" }}

<h1 style="color: red;">Page 1!</h1>

{{ end }}

Sub template 2 - about.gohtml

{{ define "content" }}

<h1 style="color: blue;">Page 2!</h1>

{{ end }}

The Go code

package main

import (
	"html/template"
	"net/http"
	"strings"
)

var tpl *template.Template

func init() {
	tpl = template.Must(template.ParseGlob("templates/*.gohtml"))
}

func main() {
	http.HandleFunc("/", index)
	http.ListenAndServe(":8080", nil)
}

func index(w http.ResponseWriter, r *http.Request) {

	path := strings.Trim(r.URL.Path, "/")
	switch path {
	case "":
		path = ("index.gohtml")
	default:
		path = (path + ".gohtml")
	}

	err := tpl.ExecuteTemplate(w, "layout", path)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

I have also tried to ParseFiles before Execute with no luck. Any tip how to do this?


(Johan Dahl) #2

Is this what you are trying to accomplish?

https://www.htmlgoodies.com/beyond/reference/nesting-templates-with-go-web-programming.html


(Sibert) #3

Yes. ONE layout with MANY different templates.

This code works:

var tpl *template.Template

func init() {tpl = template.Must(template.ParseGlob("templates/*.gohtml"))
}

func main() {
   http.HandleFunc("/", index)
   http.ListenAndServe(":8080", nil)
}

func index(w http.ResponseWriter, r *http.Request) {
   tpl.ExecuteTemplate(w, "layout", nil)
}

But as soon as I try to set another content:

var tpl *template.Template

func init() {tpl = template.Must(template.ParseGlob("templates/*.gohtml"))
}

func main() {
   http.HandleFunc("/", index)
   http.ListenAndServe(":8080", nil)
}

func index(w http.ResponseWriter, r *http.Request) {
   tpl, _ = template.ParseFiles("layout.gohtml", "index.gohtml")
   tpl.ExecuteTemplate(w, "layout", nil)
}

Got tons of errors

2019/02/17 15:59:07 http: panic serving 127.0.0.1:52020:
runtime error: invalid memory address or nil pointer dereference etc

So any tip is welcome!


(Sibert) #4

At last I got it to work, but only when not following the manuals.

Solution Part 1

Skip {{define…}} and {{end}} in templates. Weird…

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Go Web Programming</title>
  </head>
  <body>
    layout level
    {{ template "content" . }}
  </body>
</html>

As well as in subtemplates…

<p>subpage</p>

Solution Part 2

I found a code snippet with AddParsTree and here is the code (simplified with no error handling)

package main

import (
    "html/template"
    "net/http"
    "strings"
)

var tpl *template.Template

func init() {
    tpl = template.Must(template.ParseGlob("templates/*.html"))
}

func main() {
    http.HandleFunc("/", index)
    http.ListenAndServe(":8080", nil)
}

func index(w http.ResponseWriter, r *http.Request) {

    path := strings.Trim(r.URL.Path, "/")
    switch path {
    case "":
        path = ("home.html")
    default:
        path = (path + ".html")
    }

    layout := tpl.Lookup("layout.html")
    layout, _ = layout.Clone()
    t := tpl.Lookup(path)
    _, _ = layout.AddParseTree("content", t.Tree)
    layout.Execute(w, "")

I do not really understand why I have to disobey the manuals to get it to work. Any comments that enlighten me will be appreciated.


(system) closed #5

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.