wview

package module
v0.0.0-...-c1bd0ee Latest Latest
Warning

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

Go to latest
Published: Dec 5, 2021 License: MIT Imports: 11 Imported by: 0

README

GitHub Repo stars GitHub GitHub go.mod Go version GitHub all releases GitHub CI Status Go Report Card Go.Dev reference codecov

wview

wview is a lightweight, minimalist and idiomatic template library based on golang html/template for building Go web application.

Contents

Install

go get github.com/wyy-go/wview

Features

  • Lightweight - use golang html/template syntax.
  • Easy - easy use for your web application.
  • Fast - Support configure cache template.
  • Include syntax - Support include file.
  • Master layout - Support configure master layout file.
  • Extension - Support configure template file extension.
  • Easy - Support configure templates directory.
  • Auto reload - Support dynamic reload template(disable cache mode).
  • Multiple Engine - Support multiple templates for frontend and backend.
  • No external dependencies - plain ol' Go html/template.
  • Gorice - Support gorice for package resources.
  • Gin/Iris/Echo/Chi - Support gin framework, Iris framework, echo framework, go-chi framework.

Supports

Usage

Overview

Project structure:

|-- app/views/
    |--- index.html          
    |--- page.html
    |-- layouts/
        |--- footer.html
        |--- master.html

Use default instance:

//write http.ResponseWriter
//"index" -> index.html
wview.Render(writer, http.StatusOK, "index", wview.M{})

Use new instance with config:

wv := wview.New(wview.Config{
    Root:      "views",
    Extension: ".tpl",
    Master:    "layouts/master",
    Partials:  []string{"partials/ad"},
    Funcs: template.FuncMap{
        "sub": func(a, b int) int {
            return a - b
        },
        "copy": func() string {
            return time.Now().Format("2006")
        },
    },
    DisableCache: true,
    Delims:    Delims{Left: "{{", Right: "}}"},
})

//Set new instance
wview.Use(wv)

//write http.ResponseWriter
wview.Render(writer, http.StatusOK, "index", wview.M{})

Use multiple instance with config:

//============== Frontend ============== //
wvFrontend := wview.New(wview.Config{
    Root:      "views/frontend",
    Extension: ".tpl",
    Master:    "layouts/master",
    Partials:  []string{"partials/ad"},
    Funcs: template.FuncMap{
        "sub": func(a, b int) int {
            return a - b
        },
        "copy": func() string {
            return time.Now().Format("2006")
        },
    },
    DisableCache: true,
    Delims:       Delims{Left: "{{", Right: "}}"},
})

//write http.ResponseWriter
wvFrontend.Render(writer, http.StatusOK, "index", wview.M{})

//============== Backend ============== //
wvBackend := wview.New(wview.Config{
    Root:      "views/backend",
    Extension: ".tpl",
    Master:    "layouts/master",
    Partials:  []string{"partials/ad"},
    Funcs: template.FuncMap{
        "sub": func(a, b int) int {
            return a - b
        },
        "copy": func() string {
            return time.Now().Format("2006")
        },
    },
    DisableCache: true,
    Delims:       Delims{Left: "{{", Right: "}}"},
})

//write http.ResponseWriter
wvBackend.Render(writer, http.StatusOK, "index", wview.M{})

Config
wview.Config{
    Root:      "views", //template root path
    Extension: ".tpl", //file extension
    Master:    "layouts/master", //master layout file
    Partials:  []string{"partials/head"}, //partial files
    Funcs: template.FuncMap{
        "sub": func(a, b int) int {
            return a - b
        },
        // more funcs
    },
    DisableCache: false, //if disable cache, auto reload template file for debug.
    Delims:       Delims{Left: "{{", Right: "}}"},
}
Include syntax
//template file
{{include "layouts/footer"}}
Render name:

Render name use index without .html extension, that will render with master layout.

  • "index" - Render with master layout.
  • "index.html" - Not render with master layout.
Notice: `.html` is default template extension, you can change with config

Render with master

//use name without extension `.html`
wview.Render(w, http.StatusOK, "index", wview.M{})

The w is instance of http.ResponseWriter

Render only file(not use master layout)

//use full name with extension `.html`
wview.Render(w, http.StatusOK, "page.html", wview.M{})
Custom template functions

We have two type of functions global functions, and temporary functions.

Global functions are set within the config.

wview.Config{
	Funcs: template.FuncMap{
		"reverse": e.Reverse,
	},
}
//template file
{{ reverse "route-name" }}

Temporary functions are set inside the handler.

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
	err := wview.Render(w, http.StatusOK, "index", wview.M{
		"reverse": e.Reverse,
	})
	if err != nil {
		fmt.Fprintf(w, "Render index error: %v!", err)
	}
})
//template file
{{ call $.reverse "route-name" }}

Examples

See _examples/ for a variety of examples.

Basic example
package main

import (
	"fmt"
	"github.com/wyy-go/wview"
	"net/http"
)

func main() {

	//render index use `index` without `.html` extension, that will render with master layout.
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		err := wview.Render(w, http.StatusOK, "index", wview.M{
			"title": "Index title!",
			"add": func(a int, b int) int {
				return a + b
			},
		})
		if err != nil {
			fmt.Fprintf(w, "Render index error: %v!", err)
		}

	})

	//render page use `page.tpl` with '.html' will only file template without master layout.
	http.HandleFunc("/page", func(w http.ResponseWriter, r *http.Request) {
		err := wview.Render(w, http.StatusOK, "page.html", wview.M{"title": "Page file title!!"})
		if err != nil {
			fmt.Fprintf(w, "Render page.html error: %v!", err)
		}
	})

	fmt.Println("Listening and serving HTTP on :9090")
	http.ListenAndServe(":9090", nil)

}

Project structure:

|-- app/views/
    |--- index.html          
    |--- page.html
    |-- layouts/
        |--- footer.html
        |--- master.html
    

See in "examples/basic" folder

Basic example

Gin example
go get github.com/wyy-go/wview/plugin/ginview
package main

import (
	"github.com/wyy-go/wview/plugin/ginview"
	"github.com/gin-gonic/gin"
	"net/http"
)

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

	//new template engine
	router.HTMLRender = wview.Default()

	router.GET("/", func(ctx *gin.Context) {
		//render with master
		ctx.HTML(http.StatusOK, "index", gin.H{
			"title": "Index title!",
			"add": func(a int, b int) int {
				return a + b
			},
		})
	})

	router.GET("/page", func(ctx *gin.Context) {
		//render only file, must full name with extension
		ctx.HTML(http.StatusOK, "page.html", gin.H{"title": "Page file title!!"})
	})

	router.Run(":9090")
}

Project structure:

|-- app/views/
    |--- index.html          
    |--- page.html
    |-- layouts/
        |--- footer.html
        |--- master.html
    

See in "examples/basic" folder

Gin example

Iris example
$ go get github.com/wyy-go/wview/plugin/irisview
package main

import (
	"github.com/wyy-go/wview/main/irisview"
	"github.com/kataras/iris/v12"
)

func main() {
	app := iris.New()

	// Register the wview template engine.
	app.RegisterView(irisview.Default())

	app.Get("/", func(ctx iris.Context) {
		// Render with master.
		ctx.View("index", iris.Map{
			"title": "Index title!",
			"add": func(a int, b int) int {
				return a + b
			},
		})
	})

	app.Get("/page", func(ctx iris.Context) {
		// Render only file, must full name with extension.
		ctx.View("page.html", iris.Map{"title": "Page file title!!"})
	})

	app.Listen(":9090")
}

Project structure:

|-- app/views/
    |--- index.html          
    |--- page.html
    |-- layouts/
        |--- footer.html
        |--- master.html
    

See in "examples/iris" folder

Iris example

Iris multiple example
package main

import (
	"html/template"
	"time"

	"github.com/wyy-go/wview"
	"github.com/wyy-go/wview/plugin/irisview"
	"github.com/kataras/iris/v12"
)

func main() {
	app := iris.New()

	// Register a new template engine.
	app.RegisterView(irisview.New(wview.Config{
		Root:      "views/frontend",
		Extension: ".html",
		Master:    "layouts/master",
		Partials:  []string{"partials/ad"},
		Funcs: template.FuncMap{
			"copy": func() string {
				return time.Now().Format("2006")
			},
		},
		DisableCache: true,
	}))

	app.Get("/", func(ctx iris.Context) {
		ctx.View("index", iris.Map{
			"title": "Frontend title!",
		})
	})

	//=========== Backend ===========//

	// Assign a new template middleware.
	mw := irisview.NewMiddleware(wview.Config{
		Root:      "views/backend",
		Extension: ".html",
		Master:    "layouts/master",
		Partials:  []string{},
		Funcs: template.FuncMap{
			"copy": func() string {
				return time.Now().Format("2006")
			},
		},
		DisableCache: true,
	})

	backendGroup := app.Party("/admin", mw)

	backendGroup.Get("/", func(ctx iris.Context) {
		// Use the ctx.View as you used to. Zero changes to your codebase,
		// even if you use multiple templates.
		ctx.View("index", iris.Map{
			"title": "Backend title!",
		})
	})

	app.Listen(":9090")
}

Project structure:

|-- app/views/
    |-- fontend/
        |--- index.html
        |-- layouts/
            |--- footer.html
            |--- head.html
            |--- master.html
        |-- partials/
     	   |--- ad.html
    |-- backend/
        |--- index.html
        |-- layouts/
            |--- footer.html
            |--- head.html
            |--- master.html
        
See in "examples/iris-multiple" folder

Iris multiple example

Echo example

Echo <=v3 version:

go get github.com/wyy-go/wview/plugin/echoview

Echo v4 version:

go get github.com/wyy-go/wview/plugin/echoview-v4
package main

import (
	"github.com/wyy-go/wview/plugin/echoview"
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	"net/http"
)

func main() {

	// Echo instance
	e := echo.New()

	// Middleware
	e.Use(middleware.Logger())
	e.Use(middleware.Recover())

	//Set Renderer
	e.Renderer = echoview.Default()

	// Routes
	e.GET("/", func(c echo.Context) error {
		//render with master
		return c.Render(http.StatusOK, "index", echo.Map{
			"title": "Index title!",
			"add": func(a int, b int) int {
				return a + b
			},
		})
	})

	e.GET("/page", func(c echo.Context) error {
		//render only file, must full name with extension
		return c.Render(http.StatusOK, "page.html", echo.Map{"title": "Page file title!!"})
	})

	// Start server
	e.Logger.Fatal(e.Start(":9090"))
}

Project structure:

|-- app/views/
    |--- index.html          
    |--- page.html
    |-- layouts/
        |--- footer.html
        |--- master.html
    

See in "examples/basic" folder

Echo example Echo v4 example

Go-chi example
package main

import (
	"fmt"
	"github.com/wyy-go/wview"
	"github.com/go-chi/chi"
	"net/http"
)

func main() {

	r := chi.NewRouter()

	//render index use `index` without `.html` extension, that will render with master layout.
	r.Get("/", func(w http.ResponseWriter, r *http.Request) {
		err := wview.Render(w, http.StatusOK, "index", wview.M{
			"title": "Index title!",
			"add": func(a int, b int) int {
				return a + b
			},
		})
		if err != nil {
			fmt.Fprintf(w, "Render index error: %v!", err)
		}
	})

	//render page use `page.tpl` with '.html' will only file template without master layout.
	r.Get("/page", func(w http.ResponseWriter, r *http.Request) {
		err := wview.Render(w, http.StatusOK, "page.html", wview.M{"title": "Page file title!!"})
		if err != nil {
			fmt.Fprintf(w, "Render page.html error: %v!", err)
		}
	})

	fmt.Println("Listening and serving HTTP on :9090")
	http.ListenAndServe(":9090", r)

}

Project structure:

|-- app/views/
    |--- index.html          
    |--- page.html
    |-- layouts/
        |--- footer.html
        |--- master.html
    

See in "examples/basic" folder

Chi example

Advance example
package main

import (
	"fmt"
	"github.com/wyy-go/wview"
	"html/template"
	"net/http"
	"time"
)

func main() {

	wv := wview.New(wview.Config{
		Root:      "views",
		Extension: ".tpl",
		Master:    "layouts/master",
		Partials:  []string{"partials/ad"},
		Funcs: template.FuncMap{
			"sub": func(a, b int) int {
				return a - b
			},
			"copy": func() string {
				return time.Now().Format("2006")
			},
		},
		DisableCache: true,
	})

	//Set new instance
	wview.Use(wv)

	//render index use `index` without `.html` extension, that will render with master layout.
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		err := wview.Render(w, http.StatusOK, "index", wview.M{
			"title": "Index title!",
			"add": func(a int, b int) int {
				return a + b
			},
		})
		if err != nil {
			fmt.Fprintf(w, "Render index error: %v!", err)
		}

	})

	//render page use `page.tpl` with '.html' will only file template without master layout.
	http.HandleFunc("/page", func(w http.ResponseWriter, r *http.Request) {
		err := wview.Render(w, http.StatusOK, "page.tpl", wview.M{"title": "Page file title!!"})
		if err != nil {
			fmt.Fprintf(w, "Render page.html error: %v!", err)
		}
	})

	fmt.Println("Listening and serving HTTP on :9090")
	http.ListenAndServe(":9090", nil)
}

Project structure:

|-- app/views/
    |--- index.tpl          
    |--- page.tpl
    |-- layouts/
        |--- footer.tpl
        |--- head.tpl
        |--- master.tpl
    |-- partials/
        |--- ad.tpl
    

See in "examples/advance" folder

Advance example

Multiple example
package main

import (
	"html/template"
	"net/http"
	"time"

	"github.com/wyy-go/wview"
	"github.com/gin-gonic/gin"
)

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

	//new template engine
	router.HTMLRender = gintemplate.New(gintemplate.TemplateConfig{
		Root:      "views/fontend",
		Extension: ".html",
		Master:    "layouts/master",
		Partials:  []string{"partials/ad"},
		Funcs: template.FuncMap{
			"copy": func() string {
				return time.Now().Format("2006")
			},
		},
		DisableCache: true,
	})

	router.GET("/", func(ctx *gin.Context) {
		// `HTML()` is a helper func to deal with multiple TemplateEngine's.
		// It detects the suitable TemplateEngine for each path automatically.
		gintemplate.HTML(ctx, http.StatusOK, "index", gin.H{
			"title": "Fontend title!",
		})
	})

	//=========== Backend ===========//

	//new middleware
	mw := gintemplate.NewMiddleware(gintemplate.TemplateConfig{
		Root:      "views/backend",
		Extension: ".html",
		Master:    "layouts/master",
		Partials:  []string{},
		Funcs: template.FuncMap{
			"copy": func() string {
				return time.Now().Format("2006")
			},
		},
		DisableCache: true,
	})

	// You should use helper func `Middleware()` to set the supplied
	// TemplateEngine and make `HTML()` work validly.
	backendGroup := router.Group("/admin", mw)

	backendGroup.GET("/", func(ctx *gin.Context) {
		// With the middleware, `HTML()` can detect the valid TemplateEngine.
		gintemplate.HTML(ctx, http.StatusOK, "index", gin.H{
			"title": "Backend title!",
		})
	})

	router.Run(":9090")
}


Project structure:

|-- app/views/
    |-- fontend/
        |--- index.html
        |-- layouts/
            |--- footer.html
            |--- head.html
            |--- master.html
        |-- partials/
     	   |--- ad.html
    |-- backend/
        |--- index.html
        |-- layouts/
            |--- footer.html
            |--- head.html
            |--- master.html
        
See in "examples/multiple" folder

Multiple example

go.rice example
go get github.com/wyy-go/wview/plugin/gorice
package main

import (
	"fmt"
	"github.com/GeertJohan/go.rice"
	"github.com/wyy-go/wview"
	"github.com/wyy-go/wview/plugin/gorice"
	"net/http"
)

func main() {

	//static
	staticBox := rice.MustFindBox("static")
	staticFileServer := http.StripPrefix("/static/", http.FileServer(staticBox.HTTPBox()))
	http.Handle("/static/", staticFileServer)

	//new view engine
	wv := gorice.New(rice.MustFindBox("views"))
	//set engine for default instance
	wview.Use(wv)

	//render index use `index` without `.html` extension, that will render with master layout.
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		err := wview.Render(w, http.StatusOK, "index", wview.M{
			"title": "Index title!",
			"add": func(a int, b int) int {
				return a + b
			},
		})
		if err != nil {
			fmt.Fprintf(w, "Render index error: %v!", err)
		}

	})

	//render page use `page.tpl` with '.html' will only file template without master layout.
	http.HandleFunc("/page", func(w http.ResponseWriter, r *http.Request) {
		err := wview.Render(w, http.StatusOK, "page.html", wview.M{"title": "Page file title!!"})
		if err != nil {
			fmt.Fprintf(w, "Render page.html error: %v!", err)
		}
	})

	fmt.Println("Listening and serving HTTP on :9090")
	http.ListenAndServe(":9090", nil)
}

Project structure:

|-- app/views/
    |--- index.html          
    |--- page.html
    |-- layouts/
        |--- footer.html
        |--- master.html
|-- app/static/  
    |-- css/
        |--- bootstrap.css   	
    |-- img/
        |--- gopher.png

See in "examples/gorice" folder

gorice example

More examples

See _examples/ for a variety of examples.

Todo

[ ] Add Partials support directory or glob

[ ] Add functions support.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var DefaultConfig = newDefaultConfig()

DefaultConfig default config

View Source
var HTMLContentType = []string{"text/html; charset=utf-8"}

HTMLContentType const templateEngineKey = "httpx_templateEngine"

Functions

func Render

func Render(w http.ResponseWriter, status int, name string, data interface{}) error

Render render view template with default instance

func Use

func Use(engine *ViewEngine)

Use setting default instance engine

Types

type Config

type Config struct {
	Root         string           // view root
	Extension    string           // template extension
	Master       string           // template master
	Partials     []string         // template partial, such as head, foot
	Funcs        template.FuncMap // template functions
	DisableCache bool             // disable cache, debug mode
	Delims       Delims           // delimeters
	EnableEmbed  bool
	Views        embed.FS
}

Config configuration options

type Delims

type Delims struct {
	Left  string
	Right string
}

Delims delims for template

type FileHandler

type FileHandler func(config Config, tplFile string) (content string, err error)

FileHandler file handler interface

func DefaultFileHandler

func DefaultFileHandler() FileHandler

DefaultFileHandler new default file handler

type M

type M map[string]interface{}

M map interface for data

type ViewEngine

type ViewEngine struct {
	// contains filtered or unexported fields
}

ViewEngine view template engine

func Default

func Default() *ViewEngine

Default new default template engine

func New

func New(config Config) *ViewEngine

New new template engine

func (*ViewEngine) Render

func (e *ViewEngine) Render(w http.ResponseWriter, statusCode int, name string, data interface{}) error

Render render template with http.ResponseWriter

func (*ViewEngine) RenderWriter

func (e *ViewEngine) RenderWriter(w io.Writer, name string, data interface{}) error

RenderWriter render template with io.Writer

func (*ViewEngine) SetFileHandler

func (e *ViewEngine) SetFileHandler(handle FileHandler)

SetFileHandler set file handler

Jump to

Keyboard shortcuts

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