jwesonga
jwesonga

Reputation: 4383

Template inheritance loads empty page

I'm working on a basic app in Golang+AppEngine and I'm trying to implement template inheritance like Django, so far I have this code:

var TmplBasePath = "templates/"

var BasePageTmplPath = []string{TmplBasePath + "base.html"}

type Page struct {
    Title string
    Ctx   appengine.Context
}

func NewPage(r *http.Request, title string) *Page {
    return &Page{Title: title}
}

func (p *Page) Display(w http.ResponseWriter, tmplPath string) {
    tmplPath = TmplBasePath + tmplPath
    tmpl := template.New("PAGE")
    tmpl = template.Must(template.ParseFiles(BasePageTmplPath...))
    tmpl = template.Must(template.ParseFiles(tmplPath))
    if err := tmpl.Execute(w, nil); err != nil {
        p.Ctx.Errorf("%v ", err)
    }
}

func init() {
    http.HandleFunc("/", home)
}

func home(w http.ResponseWriter, r *http.Request) {
    p := NewPage(r, "home")
    p.Display(w, "index.html")

}

My templates: base.html

{{ define "PAGE"}}

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
            <title> Expat Duka </title>
        <link rel="stylesheet" href="css/bootstrap.min.css"/>
    </head>
    <body>

        <nav class="navbar navbar-inverse navbar-fixed-top">
      <div class="container">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="#">Project name</a>
        </div>
        <div id="navbar" class="collapse navbar-collapse">
          <ul class="nav navbar-nav">
            <li class="active"><a href="#">Home</a></li>
            <li><a href="#about">About</a></li>
            <li><a href="#contact">Contact</a></li>
          </ul>
        </div><!--/.nav-collapse -->
      </div>
    </nav>

         <div class="container">

      <div class="starter-template">
        {{template "CONTENT" .}}

      </div>

    </div><!-- /.container -->

    </body>
    </html>
  {{end}}

index.html

{{define "CONTENT"}}
        <h1> Welcome to Expat Duka </h1>

{{end}}

The page loads, no errors, but its blank, any idea what I'm doing wrong

Upvotes: 2

Views: 1842

Answers (1)

icza
icza

Reputation: 417412

Beforehand: Please note that there is a template.ParseFiles() function and there is a Template.ParseFiles() method. The first one returns a new template, the second you have to call on an already created template and it returns an associated template. Associated templates know about each other, so you don't even have to store the returned new template if you already stored the one whose method you called.


Firstly this is your problem:

tmpl := template.New("PAGE")
tmpl = template.Must(template.ParseFiles(BasePageTmplPath...))
tmpl = template.Must(template.ParseFiles(tmplPath))

template.ParseFiles() returns a new template (Must() just checks its parameters and returns the same). So when you create a new in the first line (with template.New()), you lose it because in the 2nd line you create another one with ParseFiles() and assign it to the same tmpl variable. And yet again you create a new, completely independent 3rd template in the 3rd line and you assign that to the tmpl variable.

These 3 templates are completely independent and they don't know about each other!

If you want the templates to know about each other (and so they can refer/include each other), use the Template.Parse() or Template.ParseFiles() methods as in this example:

tmpl := template.New("PAGE")
template.Must(tmpl.ParseFiles(BasePageTmplPath...))
template.Must(tmpl.ParseFiles(tmplPath))

And now you have multiple associated templates in the tmpl variable, execute the one you're interested in:

if err := tmpl.ExecuteTemplate(w, "index.html", nil); err != nil {
    p.Ctx.Errorf("%v ", err)
}

Notes:

Also it is very bad practice to parse templates in the handler which serves the request, it takes relatively long time. Also parsing and creating the template each time when serving a request generates lots of values in memory which are then thrown away (because they are not reused) giving additional work for the garbage collector.

Parse the templates when your application starts, store it in a variable, and you only have to execute the template when a request comes in. See this answer for more details.

Upvotes: 4

Related Questions