47

How are people handling the use of templates in their Go-based AppEngine applications?

Specifically, I'm looking for a project structure that affords the following:

  • Hierarchical (directory) structure of templates and partial templates
  • Allow me to use HTML tools/editors on my templates (embedding template text in xxx.go files makes this difficult)
  • Automatic reload of template text when on dev server

Potential stumbling blocks are:

  • template.ParseGlob() will not traverse recursively.
  • For performance reasons it has been recommended not to upload your templates as raw text files (because those text files reside on different servers than executing code).

Please note that I am not looking for a tutorial/examples of the use of the template package. This is more of an app structure question. That being said, if you have code that solves the above problems, I would love to see it. Thanks in advance.

cheeken
  • 33,663
  • 4
  • 35
  • 42
laslowh
  • 8,482
  • 5
  • 34
  • 45

2 Answers2

69

One of my favorite features of Go is the ability to easily add handlers inside of packages. This greatly simplifies the processes of writing modular code.

For Example:

File Structure

|-- app.yaml
|-- app
|   +-- http.go
|-- templates
|   +-- base.html
+-- github.com
    +-- storeski
        +-- appengine
            |-- products
            |   |-- http.go
            |   +-- templates
            |       |-- list.html
            |       +-- detail.html 
            +-- account
                |-- http.go
                +-- templates
                    |-- overview.html
                    +-- notifications.html 

Each packages has a http.go file that takes ownership of a url prefix. For example the products package under github.com/storeski/appengine/products would own any inbound url starting with /products.

With this modular approach it is beneficial to store the templates within the products package. If you would like to maintain a consistant base template for the site you can establish a convention where you extend templates/base.html.

Example

templates/base.html

<!DOCTYPE HTML>
<html>
  <head>
    <title>{{.Store.Title}}</title>
  </head>

  <body>
    <div id="content">
      {{template "content" .}}
    </div>
  </body>
</html>

github.com/storeski/appengine/products/templates/list.html

{{define "content"}}
  <h1> Products List </h1>
{{end}}

github.com/storeski/appengine/products/http.go

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

var listTmpl = template.Must(template.ParseFiles("templates/base.html",
  "github.com/storeski/appengine/products/templates/list.html"))

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

  tc := make(map[string]interface{})
  tc["Store"] = Store
  tc["Products"] = Products

  if err := listTmpl.Execute(w, tc); err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
  }
}

This approach is very exciting because it makes the sharing of apps/package trivial. If I write a package that handles authentication which takes ownership of the /auth url. Any developer that, then, adds the package to their product root instantly has all of the functionality. All they have to do is create a base template (templates/base.html) and direct their users to /auth.

Kyle Finley
  • 11,842
  • 6
  • 43
  • 64
  • Very nice, Kyle. How do you suggest to handle models using this layout? I mean declaring the types inside the http.go or maybe create a model.go file?. Thanks in advance. – Greivin López Sep 28 '13 at 15:21
  • Thanks @GreivinLópez. Sure I think for simply models it makes sense to place the code in a `model.go` file. For large models or models that will be used in multiple views, you may want to decouple the types completely -- moving them to their own package. [Here's and example](https://github.com/gaego/user) (outdated now) of a user model that lives in it's own package. – Kyle Finley Sep 28 '13 at 16:17
  • What does your app.yaml look like to expose the templates to the app but not the web? – Patrick Linskey Dec 23 '13 at 01:07
  • 2
    Hi @PatrickLinskey, your `app.yaml` file should *not* include the `templates` directory. App Engine treats directories listed in the `app.yaml` file as static resources, but any directories not listed in the `app.yaml` file will be accessible from code. I hope that helps. – Kyle Finley Dec 23 '13 at 02:50
  • @KyleFinley thanks, that was useful. Additionally, it looks like the path structure is a bit different than what I expected. I downloaded my deployed app with appcfg.py and looked at the deployed structure and changed my layout accordingly so that dev and production package structures now line up. – Patrick Linskey Dec 31 '13 at 11:55
  • @KyleFinley Why is your app.yaml, base template etc not inside github.com/storeski? – Mark Apr 28 '14 at 15:33
  • As far as I can tell, the `template.ParseFiles` line doesn't work because "github.com" is not a realistic path? unless it makes the correct assumption about the working directory. – Kevin Burke Nov 29 '14 at 17:14
  • What if i want to have many http handlers, then i should create something like `tmpls := map[string]*Template { "listTmpl": template.Must(..), "editTmpl": .. }`, and then in handler: `tmpls["listTmpl"].Execute(...`. It looks like overhead because template object maintains its own map of templates, or there is better way ? – Dfr Dec 09 '14 at 19:39
  • @KevinBurke, yes it assumes that the directory contains a folder labeled "github.com" – Kyle Finley Dec 10 '14 at 18:59
  • @Dfr, I don't completely understand your question. Would you mind creating a separate stackoverflow question for it? – Kyle Finley Dec 10 '14 at 19:02
  • Is there a way to pass the template directory to your `products` package without hard-coding it in? – 425nesp Jan 16 '15 at 23:53
  • @425nesp, you can use [`template.Parse`](http://golang.org/pkg/html/template/#Template.Parse) to parse a string. The string could be stored in the datastore for example. – Kyle Finley Jan 17 '15 at 02:19
  • Oh, okay. Thanks! Got some more questions for ya. If `list.html` contains `{{define "content"}}` and `products/http.go` parses the base and content template outside of the handlers, then how do you execute `detail.html`? Would you have to call `ParseFiles` again, but with `detail.html` in `package products`? Does each package only have one handler or can it have multiple handlers? – 425nesp Jan 17 '15 at 06:06
  • 1
    @425nesp, yes, just create another `var detailTmpl = ...` replacing `list.html` with `detail.html` and create a `func detailHandler(...` all in the same `http.go` file. – Kyle Finley Jan 17 '15 at 15:35
0

Apologies beforehand because this post isn't what you're really looking for and you may have already heard what I'm about to say a million times. It's better than no posts at all though, so here it goes:

Go 1 will be released very soon (in a week or two). I'm positive that App Engine will be switching to supporting Go 1 over r60 relatively soon after. The template corelibs (among other libs) got played with a decent amount in that time so it's kind of a mess to find the popular way of doing things relevant to oneself because of the many changes going through the language.

That being said, I've seen quite a few people tackling this different ways, but very few of them were AppEngine specific because most of the working being done in Go is kept up-to-date with the language (which has long been non-compatible with r60). If you want to see some of the code that people have been using for similar projects, you should hop on IRC and ask. Templates are a popular topic and I've only very used basic functionality with them -- I've never touched sets. The IRC is super friendly and you can learn a lot there. It's definitely the best resource besides the docs right now for the language. In case you don't already know the IRC channel is #go-nuts on FreeNode.

Thanks for reading and good luck developing on App Engine. I hope the updates to Go 1 go swiftly.

Jimmy Zelinskie
  • 1,450
  • 2
  • 12
  • 12
  • Thanks. The beta for an SDK based on Go1 is already out: https://groups.google.com/forum/?fromgroups#!topic/google-appengine-go/IlF2b1K-rXY The new templates packages have been relatively stable for a few months, so I was hoping that some thinking had been done about these issues. I'd be happy even for answers not specific to AppEngine. – laslowh Mar 06 '12 at 14:26
  • But I will take your advice and ask on IRC. I will report back. – laslowh Mar 06 '12 at 14:26
  • IRC wasn't very fruitful, but there's this discussion on the mailing list: https://groups.google.com/forum/?fromgroups#!topic/google-appengine-go/43-nFI9ryU4 – laslowh Mar 07 '12 at 21:58