Brian Voelker
Brian Voelker

Reputation: 859

Global template data

When doing ExecuteTemplate I see all examples using &whateversruct{Title: "title info", Body: "body info"} to send data to the template to replace the info. I was wondering if its possible to not have to create a struct outside my handler function because every handler function I have is not going to have the same Title, Body. It would be nice to be able to send it a map that replaces the template info. Any thoughts or ideas?

Currently - loosely written

type Info struct {
    Title string
    Body string
}

func View(w http.ResponseWriter) {
    temp.ExecuteTemplate(w, temp.Name(), &Info{Title: "title", Body: "body"})
}

Just seems like creating the struct is unnecessary. And the struct would not be the same for each function you create. So you would have to create a struct for each function (that I know of).

Upvotes: 4

Views: 8067

Answers (4)

Shawn Mire
Shawn Mire

Reputation: 306

An important distinction between applying a template to a struct vs. to a map: with a map, you can make references in your template which don't exist in your map; the template will execute with no errors and the references will just be empty. If you process against a struct and make a reference to something that doesn't exist in your struct, the template execution returns an error.

Referencing items which don't exist in your map can be useful. Consider the menu.html and the getPage() function in views.go in this sample webapp: https://bitbucket.org/jzs/sketchground/src. By using a map for the menu, the active menu item is easily highlighted.

A simple illustration of this difference:

package main

import (
    "fmt"
    "html/template"
    "os"
)

type Inventory struct {
    Material string
    Count    uint
}

func main() {
    // prep the template
    tmpl, err := template.New("test").Parse("{{.Count}} items are made of {{.Material}} - {{.Foo}}\n")
    if err != nil {
        panic(err)
    }

    // map first
    sweaterMap := map[string]string{"Count": "17", "Material": "wool"}
    err = tmpl.Execute(os.Stdout, sweaterMap)
    if err != nil {
        fmt.Println("Error!")
        fmt.Println(err)
    }

    // struct second
    sweaters := Inventory{"wool", 17}
    err = tmpl.Execute(os.Stdout, sweaters)
    if err != nil {
        fmt.Println("Error!")
        fmt.Println(err)
    }
}

Upvotes: 6

jimt
jimt

Reputation: 26410

To augment Kevin's answer: An anonymous struct will yield much the same behaviour:

func View(w http.ResponseWriter) {
    data := struct {
        Title string
        Body  string
    } {
        "About page",
        "Body info",
    }

    temp.ExecuteTemplate(w, temp.Name(), &data)
}

Upvotes: 15

Brian Voelker
Brian Voelker

Reputation: 859

You are totally right Kevin!!! I like this totally better. Thanks!!!

func View(w http.ResponseWriter) {
    info := make(map[string]string)
    info["Title"] = "About Page"
    info["Body"] = "Body Info"
    temp.ExecuteTemplate(w, temp.Name(), info)
}

Upvotes: 1

Lily Ballard
Lily Ballard

Reputation: 185681

That struct is just an example. You could pass in the struct from the outside too, or you could use a map as you suggested. A struct is nice because the type of the struct can document what fields the template expects, but it's not required.

All of these should work:

func View(w http.ResponseWriter, info Info) {
    temp.ExecuteTemplate(w, temp.Name(), &info)
}

func View(w http.ResponseWriter, info *Info) {
    temp.ExecuteTemplate(w, temp.Name(), info)
}

func View(w http.ResponseWriter, info map[string]interface{}) {
    temp.ExecuteTemplate(w, temp.Name(), info)
}

Upvotes: 10

Related Questions