htmlgowebwebservergo-templates

How to assign a variable of a go template inside a go template?


I'm just starting to use golang and the template system to redevelop my webserver. Now I just want to write constant variables for each website but I don't even really know what I'm searching. I hope some one can help.

I have this gohtml file for the "base" of every html File

    {{define "topdoc"}}
    <!DOCTYPE html>
    <html lang="en" data-bs-theme="dark">
    <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>{{.title}}</title>
        <!-- Bootstrap -->
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet"
              integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ"
              crossorigin="anonymous">
    </head>
    <body>
{{end}}

{{define "botdoc"}}
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js"
            integrity="sha384-ENjdO4Dr2bkBIFxQpeoTz1HIcje39Wm4jDKdf19U8gI4ddQ3GYNS7NTKfAdVQSZe"
            crossorigin="anonymous"></script>
    </body>
    </html>
{{end}}

Than I want to change the title and later for example meta data description and stuff like that the same way.

{{template "topdoc" .}}
{{template "navbar"}}
HOME

{{template "botdoc"}}

Navbar is defined in another file.

Now I want to give the variable in this file like

{{template "topdoc" .title="Home" .otherParam="Checking..."}}
{{template "navbar"}}
HOME

{{template "botdoc"}}

Maybe someon can help me with this very trivial issue.

When I use this method

{{define "title"}}Home{{end}}
{{template "topdoc"}}
{{template "navbar"}}
HOME

{{template "botdoc"}}

And load the base file first, it shows a blank website.

I load the template files like this:

func main() {
    r := gin.Default()

    tmpl = make(map[string]*template.Template)

    // Load templates files
    templateFiles := []string{}

    fmt.Println("Loading templates...")
    // Walk through the "templates" folder and all its subdirectories
    nerr := filepath.Walk("main/web/assets/templates", func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }

        // Check if the file is an HTML templates
        if !info.IsDir() && strings.HasSuffix(info.Name(), ".gohtml") {
            // Replace backslashes with forward slashes (for Windows compatibility)
            templateName := strings.Replace(path, "\\", "/", -1)

            // Parse the file and add it to the "tmpl" map
            templateFiles = append(templateFiles, path)

            //console log
            fmt.Print(templateName + " ")
        }
        return nil
    })

    if nerr != nil {
        panic(nerr)
    }

    fmt.Println("\n\nLoading sites...")

    // Walk through the "public" folder and all its subdirectories
    err := filepath.Walk("main/web/public", func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }

        // Check if the file is an HTML templates
        if !info.IsDir() && strings.HasSuffix(info.Name(), ".gohtml") {
            // Get the directory path (relative to the "public" folder)
            relPath, err := filepath.Rel("main/web/public", filepath.Dir(path))
            if err != nil {
                return err
            }
            // Replace backslashes with forward slashes (for Windows compatibility)
            templateName := strings.Replace(relPath, "\\", "/", -1)

            // Parse the file and add it to the "tmpl" map
            parsing := []string{}
            parsing = append(parsing, templateFiles...)
            parsing = append(parsing, path)

            fmt.Println(parsing)

            tmpl[templateName] = template.Must(template.ParseFiles(parsing...))

            // If the path is empty, default to "index"
            if templateName == "." {
                templateName = ""
            }

            // Register the templates with the appropriate route
            r.GET("/"+templateName, handler)
        }

        return nil
    })
    if err != nil {
        panic(err)
    }

    r.Run()
}

Solution

  • This is usually achieved using template composition. In your "topdoc" template, simply call other templates:

    {{define "topdoc"}}
    ...
    {{template "title"}}
    ...
    {{end}}
    

    And define the "title" template as

    {{define "title"}}Default title{{end}}
    

    Then, you can override the "title" template with a redefinition of it in a separate file:

    {{define "title"}}New title{{end}}
    {{define "someTemplate}}
    {{template "topdoc"}}
    ...
    {{end}}
    

    You have to compose these different template files so that you load the "topdoc" first (which defines the default "title"), and then load the template redefining the "title".