From a python programmer's perspective, these are lessons I've learned from developing a web app using the Go programming language.

After understanding how to structure a go web app and route requests to a view handler, the next logical step was to use a template to render an HTML response. One of the functions I was accustomed to when I was using Django was the reverse function in Django templates. This function allowed the programmer to follow the DRY principle by referencing a URL pattern by name instead of hard-coding it in the template. I wasn't able to find something similar in Go's templating library but I was able to implement it relatively easily using the mux routing library.

First you would define your route in main or init function.

var router *mux.Router

func init() {
    router = mux.NewRouter()
    router.HandleFunc("/view/{pageName:.*}", viewPage).Name("view")
}

A viewPage function is called whenever a request is received at the URL "/view/myPage". The function is named "view" in the router and can be referenced by this name later in the template.

Below is a simple view handler that grabs a couple of template files and renders them to the response.

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

    var page = parseTemplates(
        "templates/base.html",
        "templates/view.html",
    )
    if err := page.Execute(w, data); err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
}

As you can see, the view handler passes the template files to a parseTemplates function. I created a parseTemplates function that is used by many handlers to help render the response. Below is what the function looks like.

var funcs = template.FuncMap{
    "reverse": reverse,
}

func parseTemplates(files ...string) *template.Template {
    // Uses the first template file as the base name
    name := filepath.Base(files[0])

    // Specifies a function map for this template
    t := template.New(name).Funcs(funcs)

    // Adds the rest of the template files to the template object
    t = template.Must(t.ParseFiles(files...))
    return t
}

The key thing here is the function map. The function map specifies functions that can invoked from the template. We specify a single function called "reverse" in our map. Below is the implementation.

func reverse(name string, things ...interface{}) string {
    //convert the things to strings
    strs := make([]string, len(things))
    for i, th := range things {
        strs[i] = fmt.Sprint(th)
    }
    //grab the route
    u, err := router.Get(name).URL(strs...)
    if err != nil {
        panic(err)
    }
    return u.Path
}

The reverse function accepts a "name" argument. The function then looks up the name in the mux router and returns the path to the function. A template can use the code in the following manner.

<a href="{{ reverse "view" "pageName" "myPage" }}">Link</a>

This will generate HTML that will look like the following.

<a href="/view/myPage">Link</a>

Comments

comments powered by Disqus