gin-contrib/multitemplate

repository·master·Indexed 42 days ago

https://github.com/gin-contrib/multitemplate

A custom HTML renderer for the Gin web framework that supports multiple *template.Template instances. It allows developers to define different template sets—such as layouts or partial combinations—and access them by name within a single Gin engine. It supports loading templates from files, globs, fs.FS, and strings, and provides hot reloading when Gin is running in debug mode.

Tokens
4K
Snippets
11
Records
14
Agent score
55%

What's inside multitemplate

  1. Use multitemplate with Gin

    master

    To use multitemplate with the Gin web framework, import the package and assign a multitemplate.Renderer to the router.HTMLRender field. This allows you to define multiple template sets (each with its own base files) that can be called by name via c.HTML().

    package main
    
    import (
      "github.com/gin-contrib/multitemplate"
      "github.com/gin-gonic/gin"
    )
    
    func createMyRender() multitemplate.Renderer {
      r := multitemplate.NewRenderer()
      // Register a template named "index" using these files
      r.AddFromFiles("index", "templates/base.html", "templates/index.html")
      // Register a template named "article" using a different set of files
      r.AddFromFiles("article", "templates/base.html", "templates/index.html", "templates/article.html")
      return r
    }
    
    func main() {
      router := gin.Default()
      router.HTMLRender = createMyRender()
    
      router.GET("/", func(c *gin.Context) {
        c.HTML(200, "index", gin.H{
          "title": "Html5 Template Engine",
        })
      })
    
      router.Run(":8080")
    }
  2. Implement template inheritance with multitemplate

    master

    You can approximate html/template inheritance by dynamically generating template sets. By iterating through an includes/ directory and pairing each include file with a set of layouts/ files, you can create a unique named template for every combination.

    Example pattern:

    1. Load all layout files.
    2. Load all include files.
    3. For each include, create a new template set containing all layouts plus that specific include file.
    4. Use the base name of the include file as the template name.
    func loadTemplates(templatesDir string) multitemplate.Renderer {
      r := multitemplate.NewRenderer()
    
      layouts, _ := filepath.Glob(templatesDir + "/layouts/*.html")
      includes, _ := filepath.Glob(templatesDir + "/includes/*.html")
    
      for _, include := range includes {
        layoutCopy := make([]string, len(layouts))
        copy(layoutCopy, layouts)
        files := append(layoutCopy, include)
        // Use the filename (e.g., "header.html") as the template name
        r.AddFromFiles(filepath.Base(include), files...)
      }
      return r
    }
  3. Register templates with AddFromFiles

    master

    The AddFromFiles method is used to create a named template set by combining multiple files. The first argument is the name of the template, and the subsequent arguments are the paths to the files that compose it.

    r.AddFromFiles(name string, files ...string)

    r := multitemplate.NewRenderer()
    r.AddFromFiles("index", "templates/base.html", "templates/index.html")
  4. Register templates with the Renderer

    master

    The Renderer interface provides several methods to add named templates to the registry. Each method returns a *template.Template which can be used for further inspection or configuration.

    Loading from Files and Globs

    • Add(name string, tmpl *template.Template): Adds an existing *template.Template object.
    • AddFromFiles(name string, files ...string) *template.Template: Loads templates from a list of file paths.
    • AddFromGlob(name, glob string) *template.Template: Loads templates matching a specific glob pattern.
    • AddFromFilesFuncs(name string, funcMap template.FuncMap, files ...string) *template.Template: Loads templates from files and applies a template.FuncMap.
    • AddFromFilesFuncsWithOptions(name string, funcMap template.FuncMap, options TemplateOptions, files ...string) *template.Template: Loads templates from files with a FuncMap and additional TemplateOptions.
  5. Add templates to the Render instance

    master

    The Render type provides several methods to register templates using different sources. All methods will panic if the template name is empty, the template is nil, or the name already exists in the renderer.

    From Files

    • Add(name string, tmpl *template.Template): Adds an existing *template.Template object.
    • AddFromFiles(name string, files ...string): Parses files from the local filesystem.
    • AddFromGlob(name, glob string): Parses files matching a glob pattern.
    • AddFromFilesFuncs(name string, funcMap template.FuncMap, files ...string): Parses files with a custom template.FuncMap.
    • AddFromFilesFuncsWithOptions(name string, funcMap template.FuncMap, options TemplateOptions, files ...string): Parses files with a custom FuncMap and custom delimiters.

    From Filesystem (fs.FS)

    • AddFromFS(name string, fsys fs.FS, files ...string): Parses files from an fs.FS (e.g., embed.FS).
    • AddFromFSFuncs(name string, funcMap template.FuncMap, fsys fs.FS, files ...string): Parses files from an fs.FS with a custom FuncMap.

    From Strings

    • AddFromString(name, templateString string): Parses a single template string.
    • AddFromStringsFuncs(name string, funcMap template.FuncMap, templateStrings ...string): Parses multiple template strings with a custom FuncMap.
    • AddFromStringsFuncsWithOptions(name string, funcMap template.FuncMap, options TemplateOptions, templateStrings ...string): Parses multiple template strings with a custom FuncMap and custom delimiters.
  6. Initialize a new Render instance

    master

    Use New() to create a new Render instance. The Render type is a map of template names to *template.Template objects and implements the gin.HTMLRender interface, allowing it to be used directly with Gin's HTML() method.

    import "github.com/gin-contrib/multitemplate"
    
    renderer := multitemplate.New()
  7. Initialize a DynamicRender

    master

    Use NewDynamic() to create a DynamicRender instance. DynamicRender is a map-based renderer that allows you to dynamically add and manage templates by name. It implements both render.HTMLRender and the project's Renderer interface, making it compatible with Gin's rendering system.

    Use NewRenderer() if you want the library to automatically choose between a static Renderer (using New()) or a DynamicRender based on whether Gin is running in debug mode (gin.IsDebugging()).

    import "github.com/gin-contrib/gin-contrib/multitemplate"
    
    // Create a dynamic renderer
    renderer := multitemplate.NewDynamic()
    
    // Or use the agnostic constructor
    renderer := multitemplate.NewRenderer()
  8. Use the Renderer interface for multi-template rendering

    master

    The Renderer interface is the core component of multitemplate. It implements gin.HTMLRender, allowing it to be used directly with Gin engines. It provides methods to register multiple named templates, which can be loaded from files, globs, file systems (fs.FS), or raw strings.

    When running Gin in debug mode, the Renderer supports hot reloading, meaning changes to template files are reflected instantly without restarting the server.

    Note: You should always create a Renderer using the multitemplate.NewRenderer() constructor.

    // Example of how a Renderer is intended to be used with Gin
    // (Requires multitemplate.NewRenderer() implementation)
    
    router := gin.Default()
    renderer := multitemplate.NewRenderer()
    
    // Register a template
    renderer.AddFromFiles("index", "templates/layout.html", "templates/index.html")
    
    router.SetHTMLRender(renderer)
    
    router.GET("/", func(c *gin.Context) {
        c.HTML(http.StatusOK, "index", gin.H{"title": "Hello World"})
    })
  9. Render a specific template in Gin

    master

    To render a specific template from your Render instance within a Gin handler, use the Instance(name string, data interface{}) method. This returns a render.HTML object which is compatible with Gin's rendering engine.

    // Assuming 'renderer' is a multitemplate.Render instance
    // and 'user' is your data object
    
    router.GET("/profile", func(c *gin.Context) {
        c.Render(http.StatusOK, renderer.Instance("profile", user))
    })
  10. Add templates to DynamicRender

    master

    The DynamicRender type provides several methods to register templates using different sources. Each method associates a name (used to retrieve the template later via .Instance()) with a template built from the specified source.

    Available Methods:

    • Add(name string, tmpl *template.Template): Add an existing *template.Template object.
    • AddFromFiles(name string, files ...string): Load templates from local file paths.
    • AddFromGlob(name, glob string): Load templates using a glob pattern.
    • AddFromFS(name string, fsys fs.FS, files ...string): Load templates from an fs.FS (file system).
    • AddFromFSFuncs(name string, funcMap template.FuncMap, fsys fs.FS, files ...string): Load from fs.FS with a custom FuncMap.
    • AddFromString(name, templateString string): Load a single template from a string.
    • AddFromStringsFuncs(name string, funcMap template.FuncMap, templateStrings ...string): Load multiple template strings with a FuncMap.
    • AddFromStringsFuncsWithOptions(name string, funcMap template.FuncMap, options TemplateOptions, templateStrings ...string): Load multiple strings with a FuncMap and custom TemplateOptions (like delimiters).
    • AddFromFilesFuncs(name string, funcMap template.FuncMap, files ...string): Load from files with a FuncMap.
    • AddFromFilesFuncsWithOptions(name string, funcMap template.FuncMap, options TemplateOptions, files ...string): Load from files with a FuncMap and custom TemplateOptions.
    // Example: Adding templates from various sources
    
    // 1. From files
    renderer.AddFromFiles("user_profile", "templates/layout.html", "templates/profile.html")
    
    // 2. From a glob pattern
    renderer.AddFromGlob("admin", "templates/admin/*.html")
    
    // 3. From strings with custom delimiters
    options := multitemplate.TemplateOptions{LeftDelimiter: "[[", RightDelimiter: "]]"}
    renderer.AddFromStringsFuncsWithOptions("welcome", nil, options, "<h1>Hello {{.Name}}</h1>")
    
    // 4. From an embedded file system
    renderer.AddFromFS("static", embedFS, "templates/index.html")
  11. Configure template delimiters

    master

    You can customize the template delimiters (e.g., changing {{ and }} to something else) using TemplateOptions.

    Options and Configuration

    • NewTemplateOptions(opts ...TemplateOption): Creates a TemplateOptions struct. Defaults to {{ and }}.
    • WithLeftDelimiter(delim string): Sets the left delimiter.
    • WithRightDelimiter(delim string): Sets the right delimiter.
    • Delims(leftDelim, rightDelim string): Sets both delimiters at once.

    Usage Example

    Pass the TemplateOptions to methods like AddFromFilesFuncsWithOptions or AddFromStringsFuncsWithOptions to apply the custom delimiters to the registered template.

    import "github.com/gin-contrib/multitemplate"
    
    // Create options with custom delimiters
    opts := multitemplate.NewTemplateOptions(
        multitemplate.Delims("<%", %>"),
    )
    
    // Use options when adding a template
    renderer.AddFromStringsFuncsWithOptions(
        "custom_tmpl",
        nil, // funcMap
        opts,
        "<% print .Name %>",
    )