templates

package module
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 14, 2022 License: MIT Imports: 11 Imported by: 3

README

Introduction:

This package wraps around the golang html/templates package to provide some additional tooling and aiding in ease of use around working with HTML templates for building web pages.

Details:

  • Works with on-disk or embedded source template files.
  • Store configuration in-package (globally) or elsewhere (dependency injection).
  • Doesn't require a set directory layout.
  • Allows for inheriting some templates, such as headers and footers.
  • Group source templates files into subdirectories for organization.
  • Allows for using the same template name as long as each is in a separate subdirectory.

Getting Started:

With your templates in a directory structure similar to as follows:

/path/to/templates/
├─ header.html
├─ footer.html
├─ docs/
│  ├─ index.html
│  ├─ faq.html
├─ app/
│  ├─ index.html
│  ├─ users.html
  1. Initialize your configuration using NewConfig() or NewOnDiskConfig if you want to store your configuration elsewhere; or DefaultConfig or DefaultOnDiskConfig if you want to use the globally stored configuration.

  2. Call Build() to validate your configuration and parse your template files.

  3. Call Show(w, dir, template, interface{}) to render your parsed template and show it to the user. See more info below.

Using Embedded Files:

This package can work the files embedded via the embded package. You must have already "read" the embedded files using code similar to below prior to providing the embed.FS object to this package. Note that the path must use a forward slash separator!

package main

//go:embed path/to/templates
var embeddedFiles embed.FS

func init() {
    c := NewEmbeddedConfig(embeddedFiles, "path/to/templates", []string{"app", "docs"})
    err := c.Build()
    if err != nil {
        log.Fatal(err)
        return
    }
}

Rendering a Page:

Use code similar to the following, providing your subdirectory the template is located in, the template's name (i.e.: filename), and any data you want to inject into the template for modifying the HTML or displaying.

    func MyHttpHandler(w http.ResponseWriter, r *http.Request) {
    //nil is an interface{} value and can be replaced with anything to
    //be injected into your templates under the .Data field.
    subdir := "app"
    page := "users"
    data := struct{
        Fname  string
        Age    int
        Active bool
    }{"Mike", 46, true}
    templates.Show(w, subdir, page, data)
}

The data parameter can be any data you want, or nil, and is available at the {{.InjectedData}} field.

This package also returns some other information for use when rendering pages:

  • {{.Development}}: boolean field useful for showing a "dev" banner or altering what script are included for diagnostics.
  • {{.UseLocalFiles}}: boolean field used for toggling CSS or JS files between files served from CDN/internet or files served from your local web server/app.
  • {{.CacheBustFiles}}: a set of key-value pairs of the original filename to the filename of the cache busting version of a file for use in replacing the known original filename with the generated cache busting filename. See notes below.

Cache Busting:

This package does not force any style or type of cache busting upon you. You simply need to provide the original file's name and name of the cache busting version of the file as a key-value map.

You must know the original file name of your files (i.e.: script.min.js). If not, this won't work for you. However, if you do, and you have programatic access to the name of the cache busting version of the file, then you can provide the filename pairing and handle the replacement of the original filename in each of your HTML templates using code similar to as follows:

<html>
  <head>
    {{$originalFile := "styles.min.css"}}
	{{$cacheBustFiles := .CacheBustFiles}}

	{{/*If the key "styles.min.css" exists in $cacheBustFiles, then the associated cache-busted filename will be returned as {{.}}. */}}
	{{with index $cacheBustFiles $originalFile}}
	  {{$cacheBustedFile := .}}
	  <link rel="stylesheet" href="/static/css/{{$cacheBustedFile}}">
    {{else}}
      <link rel="stylesheet" href="/static/css/{{$originalFile}}">
    {{end}}
  </head>
</html>

Documentation

Overview

Package templates handles parsing and rendering HTML. This more-or-less wraps the golang html/template package with some tooling for storing the parsed templates, showing a requested template, and using source HTML stored in on-disk or embedded files.

Handling of HTML templates is done by parsing files in given directories and caching them for future use within your application. Templates can be stored in numerous subdirectories for ease of organization and allowing the same filename or template declaration ({{define}}) to be used. Files stored at the root templates directory are inherited into each subdirectory; this is useful for storing files with shared {{declare}} blocks that are imported into other templates stored in numerous subdirectories (ex.: header and footers).

Serving of a template is done by providing the subdirectory and name of the template (aka filename). Note that due to this, you cannot serve templates from the root directory. Again, the root directory is for storing templates shared templates between multiple subdirectories.

An example of a directory structure for storing templates is below. templates/ ├─ header.html ├─ footer.html ├─ docs/ │ ├─ index.html │ ├─ faq.html │ ├─ how-to.html ├─ app/ │ ├─ index.html │ ├─ users.html │ ├─ widgits.html

Index

Constants

This section is empty.

Variables

View Source
var (
	//ErrBasePathNotSet is returned if a user calls Save() and not path to the
	//templates was provided.
	ErrBasePathNotSet = errors.New("templates: no value set for TemplatesBasePath")

	//ErrNoSubDirsProvided is returned when no subdirectories were provided. As of
	//now we require at least one subdirectory.
	ErrNoSubDirsProvided = errors.New("templates: no template subdirectories were provided, at least one must be")

	//ErrInvalidSubDir is returned if a user calls Save() and the provided
	//subdirectory cannot be found.
	ErrInvalidSubDir = errors.New("templates: empty or all whitespace string provided for TemplatesSubDirs is not allowed")

	//ErrNoEmbeddedFilesProvided is returned when a user is using a config with embedded files
	//but no embedded files were provided.
	ErrNoEmbeddedFilesProvided = errors.New("templates: no embedded files provided")
)

errors

Functions

func Build

func Build() (err error)

Build builds the templates using the default package level config.

func CacheBustingFilePairs

func CacheBustingFilePairs(pairs map[string]string)

CacheBustingFilePairs sets the CacheBustingFilePairs field on the package level config.

func Debug added in v1.1.0

func Debug(yes bool)

Debug sets the Development field on the package level config.

func DefaultConfig

func DefaultConfig()

DefaultConfig initializes the package level config with some defaults set. This wraps NewConfig() and saves the config to the package.

func DefaultEmbeddedConfig

func DefaultEmbeddedConfig(embeddedFS embed.FS, basePath string, subdirs []string)

DefaultEmbeddedConfig initializes the package level config with the path and directories provided and some defaults.

func DefaultFuncMap

func DefaultFuncMap() template.FuncMap

DefaultFuncMap returns the list of extra funcs defined for use in templates.

func DefaultOnDiskConfig

func DefaultOnDiskConfig(basePath string, subdirs []string)

DefaultOnDiskConfig initializes the package level config with the path and directories provided and some defaults.

func Development

func Development(yes bool)

Development sets the Development field on the package level config.

func FuncAddInt

func FuncAddInt(x interface{}, y int) (z int)

FuncAddInt performs addition.

func FuncDateReformat

func FuncDateReformat(date, format string) (d string)

FuncDateReformat is used to transform a date from the yyyy-mm-dd format to another format in templates.

func FuncIndexOf

func FuncIndexOf(needle, haystack string) int

FuncIndexOf returns the position of needle in haystack. If needle does not exist in haystack, -1 is returned.

func PrintEmbeddedFileList

func PrintEmbeddedFileList(e embed.FS)

PrintEmbeddedFileList prints out the list of files embedded into the executable.

This should only be used for diagnostics purposes only to confirm which files are embedded with the //go:embed directives elsewhere in your app.

func Show

func Show(w http.ResponseWriter, subdir, templateName string, injectedData interface{})

Show handles showing a template using the default package-level config.

func UseLocalFiles

func UseLocalFiles(yes bool)

UseLocalFiles sets the UseLocalFiles field on the package level config.

Types

type Config

type Config struct {
	//Development is passed to each template when rendering the HTML to be sent to
	//the user so that the HTML can be altered based on if you are running your app
	//in a development mode/enviroment. Typically this is used to show a banner on
	//the page, loads extra diagnostic libraries or tools, and uses non-cache busted
	//static files.
	Development bool

	//UseLocalFiles is passed to each template when rendering the HTML to be sent to
	//the user so that the HTML can be altered to use locally hosted third party
	//libraries (JS, CSS) versus libraries retrieve from the internet.
	UseLocalFiles bool

	//BasePath is the absolute path to the root directory where template files are
	//stored. There should be at least one template file at this path. This path can
	//include a single level of subdirectories that contain templates.
	//
	//For embedded files (using the embed package), this path does not start with a
	//foward slash, is forward slash separated, and must be included via //go:embed
	//directives. See https://pkg.go.dev/embed for more information.
	BasePath string

	//SubDirs is a list of subdirectories off of the BasePath where you store template
	//files. This can be empty if you have no subdirectories. This must only be the
	//actual subdirectory names, not full paths; full paths will be constructed by
	//appending each SubDir to the BasePath.
	SubDirs []string

	//Extension is the extension you use for your template files. The defaults to "html".
	Extension string

	//UseEmbedded means files built into the golang executable will be used rather
	//than files stored on-disk. You must have read the embedded files, with code
	//such as var embeddedFiles embed.FS, prior and you must provide the embed.FS to
	//the field EmbeddedFS.
	UseEmbedded bool

	//EmbeddedFiles is the filesystem embedded into this executable via the embed
	//package. You must have read the embedded files, with code such as
	//`var embeddedFiles embed.FS` prior to using this package and you must set
	//UseEmbedded to true to enable use of these files.
	EmbeddedFS embed.FS

	//FuncMap is a collection of functions that you want to use in your templates to
	//augment the golang provided templating funcs. This package provides some default
	//extra funcs in templates-templatefuncs.go. See https://pkg.go.dev/text/template for
	//more info.
	//To provide extra funcs to templates, use code such as the following:
	/*
		config, err := templates..DefaultOnDiskConfig("/path/to/templates", []string{"subdir1", "subdir2"})
		if err != nil {
			//handle err
		}
		config.FuncMap = template.FuncMap{
			"indexOf":   templates.FuncIndexOf,
			"myNewFunc": myNewFunc,
		}
		err = config.Build()
		if err != nil {
			//handle err
		}
	*/
	FuncMap template.FuncMap

	//CacheBustingFilePairs is a key-value list of filesnames that match up an original
	//file name to the file's cache busting file name. This list is then passed to your
	//templates when rendered to replace the known original filename (i.e.: script.min.js)
	//with the cache busting filename that is most likely programmatically created (i.e.:
	//A1B2C3D4.script.min.js). See the package github.com/c9845/cachebusting for an example
	//implementation and tooling.
	//
	//To use the cache busting file, you would use template code similar to the following
	//to handle the filename replacement:
	/*
		<head>
			{{$originalFile := "styles.min.css"}}
			{{$cacheBustFiles := .CacheBustFiles}}

			{{/*If the key "styles.min.css" exists in $cacheBustFiles, then the associated cache-busted filename will be returned as {{.}}. *\/}}
			{{with index $cacheBustFiles $originalFile}}
			{{$cacheBustedFile := .}}
			<link rel="stylesheet" href="/static/css/{{$cacheBustedFile}}">
			{{else}}
			<link rel="stylesheet" href="/static/css/{{$originalFile}}">
			{{end}}
		</head>
	*/
	CacheBustingFilePairs map[string]string

	//Debug prints out debugging information if true.
	Debug bool
	// contains filtered or unexported fields
}

Config is the set of configuration settings for working with templates.

func GetConfig

func GetConfig() (c *Config)

GetConfig returns the current state of the package level config.

func NewConfig

func NewConfig() *Config

NewConfig returns a config for managing your templates with some defaults set.

func NewEmbeddedConfig

func NewEmbeddedConfig(embeddedFS embed.FS, basePath string, subdirs []string) *Config

NewEmbeddedConfig returns a config for managing your templates when the source files are stored embedded in the app executable.

func NewOnDiskConfig

func NewOnDiskConfig(basePath string, subdirs []string) *Config

NewOnDiskConfig returns a config for managing your templates when the source files are stored on disk.

func (*Config) Build

func (c *Config) Build() (err error)

Build handles finding the templates files, parsing them, and building the golang templates.

Templates are built for the BasePath and each subdirectory. Templates located at the BasePath are inherited into each SubDir. You can have the same template or file name in each SubDir but not in the BasePath and SubDir.

func (*Config) Show

func (c *Config) Show(w http.ResponseWriter, subdir, filename string, injectedData interface{})

Show renders a template as HTML and writes it to w. This works by taking a subdirectory's name and the name of a template file, looks up the template that was parsed earlier in Build(), and returns it with any injected data available at {{.Data}} in HTML templates.

This does not work with {{defined}}{{end}} templates. Templates must be files. A template can include {{defined}}{{end}} and {{template}}{{end}} blocks.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL