diff --git a/context.go b/context.go
index ef5c45b..770ba41 100644
--- a/context.go
+++ b/context.go
@@ -142,13 +142,13 @@ func (p *Context) JSON(code int, data interface{}) {
func (p *Context) YAP(code int, yapFile string, data interface{}) {
w := p.ResponseWriter
- t, err := p.engine.templ(yapFile)
- if err != nil {
- log.Panicf("YAP `%s`: %v\n", yapFile, err)
+ t := p.engine.templ(yapFile)
+ if t == nil {
+ log.Panicln("YAP: not find template:", yapFile)
}
h := w.Header()
h.Set("Content-Type", "text/html")
- err = t.Execute(w, data)
+ err := t.Execute(w, data)
if err != nil {
log.Panicln("YAP:", err)
}
diff --git a/demo/blog_nestetemplate/blog.go b/demo/blog_nestetemplate/blog.go
new file mode 100644
index 0000000..dd6ed3a
--- /dev/null
+++ b/demo/blog_nestetemplate/blog.go
@@ -0,0 +1,21 @@
+package main
+
+import (
+ "os"
+
+ "github.com/goplus/yap"
+)
+
+func main() {
+ y := yap.New(os.DirFS("."))
+
+ y.GET("/", func(ctx *yap.Context) {
+ ctx.TEXT(200, "text/html", `
Hello, YAP!`)
+ })
+ y.GET("/p/:id", func(ctx *yap.Context) {
+ ctx.YAP(200, "blog", yap.H{
+ "Id": ctx.Param("id"),
+ })
+ })
+ y.Run(":8888")
+}
diff --git a/demo/blog_nestetemplate/yap/blog_yap.html b/demo/blog_nestetemplate/yap/blog_yap.html
new file mode 100644
index 0000000..d280926
--- /dev/null
+++ b/demo/blog_nestetemplate/yap/blog_yap.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+ neste-template
+
+
+ {{ template "header" }}
+ neste-template
+ {{ .Id }}
+
+
\ No newline at end of file
diff --git a/demo/blog_nestetemplate/yap/layout_yap.html b/demo/blog_nestetemplate/yap/layout_yap.html
new file mode 100644
index 0000000..79aa9d3
--- /dev/null
+++ b/demo/blog_nestetemplate/yap/layout_yap.html
@@ -0,0 +1,3 @@
+{{ define "header"}}
+header
+{{ end}}
\ No newline at end of file
diff --git a/demo/classfile_nestetemplate/blog_yap.gox b/demo/classfile_nestetemplate/blog_yap.gox
new file mode 100644
index 0000000..19d4464
--- /dev/null
+++ b/demo/classfile_nestetemplate/blog_yap.gox
@@ -0,0 +1,10 @@
+get "/", ctx => {
+ ctx.html `Hello, YAP!`
+}
+get "/p/:id", ctx => {
+ ctx.yap "blog", {
+ "Id": ctx.param("id"),
+ }
+}
+
+run ":8888"
diff --git a/demo/classfile_nestetemplate/gop_autogen.go b/demo/classfile_nestetemplate/gop_autogen.go
new file mode 100644
index 0000000..9bea1c4
--- /dev/null
+++ b/demo/classfile_nestetemplate/gop_autogen.go
@@ -0,0 +1,29 @@
+// Code generated by gop (Go+); DO NOT EDIT.
+
+package main
+
+import "github.com/goplus/yap"
+
+const _ = true
+
+type blog struct {
+ yap.App
+}
+//line demo/classfile_nestetemplate/blog_yap.gox:1
+func (this *blog) MainEntry() {
+//line demo/classfile_nestetemplate/blog_yap.gox:1:1
+ this.Get("/", func(ctx *yap.Context) {
+//line demo/classfile_nestetemplate/blog_yap.gox:2:1
+ ctx.Html__1(`Hello, YAP!`)
+ })
+//line demo/classfile_nestetemplate/blog_yap.gox:4:1
+ this.Get("/p/:id", func(ctx *yap.Context) {
+//line demo/classfile_nestetemplate/blog_yap.gox:5:1
+ ctx.Yap__1("blog", map[string]string{"Id": ctx.Param("id")})
+ })
+//line demo/classfile_nestetemplate/blog_yap.gox:10:1
+ this.Run(":8888")
+}
+func main() {
+ yap.Gopt_App_Main(new(blog))
+}
diff --git a/demo/classfile_nestetemplate/yap/blog_yap.html b/demo/classfile_nestetemplate/yap/blog_yap.html
new file mode 100644
index 0000000..d280926
--- /dev/null
+++ b/demo/classfile_nestetemplate/yap/blog_yap.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+ neste-template
+
+
+ {{ template "header" }}
+ neste-template
+ {{ .Id }}
+
+
\ No newline at end of file
diff --git a/demo/classfile_nestetemplate/yap/layout_yap.html b/demo/classfile_nestetemplate/yap/layout_yap.html
new file mode 100644
index 0000000..79aa9d3
--- /dev/null
+++ b/demo/classfile_nestetemplate/yap/layout_yap.html
@@ -0,0 +1,3 @@
+{{ define "header"}}
+header
+{{ end}}
\ No newline at end of file
diff --git a/template.go b/template.go
index 41c95c1..883358f 100644
--- a/template.go
+++ b/template.go
@@ -17,9 +17,13 @@
package yap
import (
+ "fmt"
"html/template"
"io/fs"
+ "log"
+ "os"
"path/filepath"
+ "strings"
"github.com/goplus/yap/internal/templ"
)
@@ -32,8 +36,11 @@ type Template struct {
}
// NewTemplate allocates a new, undefined template with the given name.
-func NewTemplate(name string) Template {
- return Template{template.New(name)}
+func NewTemplate(name string) *Template {
+ return &Template{template.New(name)}
+}
+func (t *Template) NewTemplate(name string) *Template {
+ return &Template{Template: t.Template.New(name)}
}
func (t Template) Parse(text string) (ret Template, err error) {
@@ -49,3 +56,109 @@ func ParseFSFile(f fs.FS, file string) (t Template, err error) {
name := filepath.Base(file)
return NewTemplate(name).Parse(string(b))
}
+
+func ParseFiles(filenames ...string) (*Template, error) {
+ return parseFiles(nil, readFileOS, filenames...)
+}
+
+func (t *Template) ParseFiles(filenames ...string) (*Template, error) {
+ return parseFiles(t, readFileOS, filenames...)
+}
+
+func parseFiles(t *Template, readFile func(string) (string, []byte, error), filenames ...string) (*Template, error) {
+
+ if len(filenames) == 0 {
+ return nil, fmt.Errorf("yap/template: no files named in call to ParseFiles")
+ }
+ for _, filename := range filenames {
+ name, b, err := readFile(filename)
+ if err != nil {
+ return nil, err
+ }
+ s := string(b)
+ var tmpl *Template
+ if t == nil {
+ t = NewTemplate(name)
+ }
+ if name == t.Name() {
+ tmpl = t
+ } else {
+ tmpl = t.NewTemplate(name)
+ }
+ _, err = tmpl.Parse(s)
+ if err != nil {
+ return nil, err
+ }
+ }
+ log.Println("yap/template list:")
+ for i, t2 := range t.Templates() {
+ log.Println(i, t2.Name())
+ }
+ return t, nil
+}
+
+func ParseGlob(pattern string) (*Template, error) {
+ return parseGlob(nil, pattern)
+}
+
+func (t *Template) ParseGlob(pattern string) (*Template, error) {
+ return parseGlob(t, pattern)
+}
+
+func parseGlob(t *Template, pattern string) (*Template, error) {
+ filenames, err := filepath.Glob(pattern)
+ if err != nil {
+ return nil, err
+ }
+ if len(filenames) == 0 {
+ return nil, fmt.Errorf("html/template: pattern matches no files: %#q", pattern)
+ }
+ return parseFiles(t, readFileOS, filenames...)
+}
+
+// ParseFS is like ParseFiles or ParseGlob but reads from the file system fs
+// instead of the host operating system's file system.
+// It accepts a list of glob patterns.
+// (Note that most file names serve as glob patterns matching only themselves.)
+func ParseFS(fs fs.FS, patterns ...string) (*Template, error) {
+ return parseFS(nil, fs, patterns)
+}
+
+// ParseFS is like ParseFiles or ParseGlob but reads from the file system fs
+// instead of the host operating system's file system.
+// It accepts a list of glob patterns.
+// (Note that most file names serve as glob patterns matching only themselves.)
+func (t *Template) ParseFS(fs fs.FS, patterns ...string) (*Template, error) {
+ return parseFS(t, fs, patterns)
+}
+
+func parseFS(t *Template, fsys fs.FS, patterns []string) (*Template, error) {
+ var filenames []string
+ for _, pattern := range patterns {
+ list, err := fs.Glob(fsys, pattern)
+ if err != nil {
+ return nil, err
+ }
+ if len(list) == 0 {
+ return nil, fmt.Errorf("template: pattern matches no files: %#q", pattern)
+ }
+ filenames = append(filenames, list...)
+ }
+ return parseFiles(t, readFileFS(fsys), filenames...)
+}
+
+func readFileOS(file string) (name string, b []byte, err error) {
+ name = filepath.Base(file)
+ b, err = os.ReadFile(file)
+ return
+}
+
+func readFileFS(fsys fs.FS) func(string) (string, []byte, error) {
+ return func(file string) (name string, b []byte, err error) {
+ name = filepath.ToSlash(file)
+ // compatible yap template name for older versions of yap, without the suffix "_ yap.html"
+ name = strings.TrimSuffix(name, "_yap.html")
+ b, err = fs.ReadFile(fsys, file)
+ return
+ }
+}
diff --git a/yap.go b/yap.go
index 9380086..64eb3e8 100644
--- a/yap.go
+++ b/yap.go
@@ -17,6 +17,7 @@
package yap
import (
+ "html/template"
"io/fs"
"log"
"net/http"
@@ -32,9 +33,9 @@ type Engine struct {
router
Mux *http.ServeMux
- tpls map[string]Template
- fs fs.FS
- las func(addr string, handler http.Handler) error
+ tpl *Template
+ fs fs.FS
+ las func(addr string, handler http.Handler) error
}
// New creates a YAP engine.
@@ -65,7 +66,15 @@ func (p *Engine) initYapFS(fsys fs.FS) {
}
}
p.fs = fsys
- p.tpls = make(map[string]Template)
+}
+
+// Load template
+func (p *Engine) loadTemplate() {
+ t, err := parseFS(NewTemplate(""), p.yapFS(), []string{"*_yap.html"})
+ if err != nil {
+ log.Panicln(err)
+ }
+ p.tpl = t
}
func (p *Engine) yapFS() fs.FS {
@@ -154,20 +163,11 @@ func (p *Engine) SetLAS(listenAndServe func(addr string, handler http.Handler) e
p.las = listenAndServe
}
-func (p *Engine) templ(path string) (t Template, err error) {
- fsys := p.yapFS()
- if p.tpls == nil {
- return Template{}, os.ErrNotExist
- }
- t, ok := p.tpls[path]
- if !ok {
- t, err = ParseFSFile(fsys, path+"_yap.html")
- if err != nil {
- return
- }
- p.tpls[path] = t
+func (p *Engine) templ(path string) *template.Template {
+ if p.tpl == nil {
+ p.loadTemplate()
}
- return
+ return p.tpl.Lookup(path)
}
// SubFS returns a sub filesystem by specified a dir.