Search code examples
gogo-templates

Is it possible for a Golang template function to render another template while referring to itself?


I want to extend the default Golang template functions with a template function that renders another Golang template while that template also should have access to the function in question.

The following demonstration case should create an include template function that renders a given template which could also include the same include function. The example, however (rightfully) raises an initialization cycle error.

Is it possible for a Golang template function to render another template while referring to itself?

https://play.golang.org/p/hml-GDhV1HI

package main

import (
    "bytes"
    "errors"
    html_template "html/template"
    "os"
)

var includeFuncs = map[string]interface{}{
    "include": func(templatePath string, data interface{}) (string, error) {
        templatePath = "templates/" + templatePath
        if _, err := os.Stat(templatePath); err != nil {
            return "", errors.New("Unable to find the template file " + templatePath)
        }

        var renderedTpl bytes.Buffer
        tpl, err := html_template.New(templatePath).Funcs(GetHTMLIncludeFuncs()).Parse(templatePath)
        if err != nil {
            return "", err
        }

        if err := tpl.Execute(&renderedTpl, data); err != nil {
            return "", err
        }

        return renderedTpl.String(), nil
    },
}

func GetHTMLIncludeFuncs() html_template.FuncMap {
    return html_template.FuncMap(includeFuncs)
}

Solution

  • Use init():

    var includeFuncs = map[string]interface{}{}
    
    func includeFunc(templatePath string, data interface{}) (string, error) {...}
    
    func init() {
       includeFuncs["include"]=includeFunc
    }
    

    In the original post, you fall into an initialization loop because before the execution starts, the runtime has to initialize all initialized variables, and the compiler detects the initialization loop.

    With the above solution, the initialization can complete before the program starts because the function map is empty. Before main starts running, the init function runs and initializes the map with the function.