From d64e6f8ec33d99fa9c7b0b81c9d5883778cb8e76 Mon Sep 17 00:00:00 2001 From: Tom Wiesing Date: Thu, 20 Oct 2022 11:26:35 +0200 Subject: [PATCH] templates: Share fragments --- internal/dis/component/control/home/home.html | 70 ++++++++----------- internal/dis/component/control/home/public.go | 2 +- .../dis/component/control/info/components.go | 18 ++--- .../control/info/html/info_components.html | 4 +- .../control/info/html/info_index.html | 4 +- .../control/info/html/info_instance.html | 4 +- internal/dis/component/control/info/index.go | 4 +- .../dis/component/control/info/instance.go | 4 +- .../dis/component/control/static/assets.go | 38 +++++----- .../dis/component/control/static/templates.go | 30 ++++++++ .../base.html => static/templates/_base.html} | 13 +++- pkg/httpx/html.go | 13 +++- 12 files changed, 111 insertions(+), 93 deletions(-) create mode 100644 internal/dis/component/control/static/templates.go rename internal/dis/component/control/{info/html/base.html => static/templates/_base.html} (66%) diff --git a/internal/dis/component/control/home/home.html b/internal/dis/component/control/home/home.html index 8b1d217..0e5d6e4 100644 --- a/internal/dis/component/control/home/home.html +++ b/internal/dis/component/control/home/home.html @@ -1,40 +1,32 @@ - - - - WissKI Distillery - {{ CSS }} - - -
-

WissKI Distillery

-
-
-
-
-

- For more information, see {{ .SelfRedirect }}. -

-
- -
-

WissKIs on this Distillery

-
- - {{range .Instances}} - {{ if .Running }} -
-

{{.Slug}}

-

- {{.URL}}
-

-
- {{ end }} - {{ end }} -
+{{ template "_base.html" . }} +{{ define "title" }}WissKI Distillery{{ end }} - - - {{ JS }} - +{{ define "header/time" }} + +{{ end }} +{{ define "header"}} + +{{ end }} + +{{ define "content" }} +
+

+ For more information, see {{ .SelfRedirect }}. +

+
+ +
+

WissKIs on this Distillery

+
+ +{{range .Instances}} + {{ if .Running }} +
+

{{.Slug}}

+

+ {{.URL}}
+

+
+ {{ end }} +{{ end }} +{{ end }} diff --git a/internal/dis/component/control/home/public.go b/internal/dis/component/control/home/public.go index cf635da..0e4518a 100644 --- a/internal/dis/component/control/home/public.go +++ b/internal/dis/component/control/home/public.go @@ -51,7 +51,7 @@ func (home *Home) updateRender(ctx context.Context, io stream.IOStream) { //go:embed "home.html" var homeHTMLStr string -var homeTemplate = static.AssetsHomeHome.MustParse(nil, homeHTMLStr) +var homeTemplate = static.AssetsHomeHome.MustParseShared("home.html", homeHTMLStr) func (home *Home) homeRender() ([]byte, error) { var context HomeContext diff --git a/internal/dis/component/control/info/components.go b/internal/dis/component/control/info/components.go index 2d8a052..fa23963 100644 --- a/internal/dis/component/control/info/components.go +++ b/internal/dis/component/control/info/components.go @@ -1,29 +1,19 @@ package info import ( - _ "embed" - "html/template" "net/http" "time" + _ "embed" + "github.com/FAU-CDI/wisski-distillery/internal/dis/component/control/static" "github.com/FAU-CDI/wisski-distillery/pkg/lazy" ) -//go:embed "html/base.html" -var baseTemplateString string -var baseTemplate = template.Must(template.New("base.html").Parse(baseTemplateString)) - -func base(name string) *template.Template { - clone := template.Must(baseTemplate.Clone()) - clone.Tree.Name = name - return clone -} - //go:embed "html/info_components.html" var componentsTemplateString string -var componentsTemplate = static.AssetsComponentsIndex.MustParse( - base("info_components.html"), +var componentsTemplate = static.AssetsComponentsIndex.MustParseShared( + "info_components.html", componentsTemplateString, ) diff --git a/internal/dis/component/control/info/html/info_components.html b/internal/dis/component/control/info/html/info_components.html index adc7627..9566561 100644 --- a/internal/dis/component/control/info/html/info_components.html +++ b/internal/dis/component/control/info/html/info_components.html @@ -1,8 +1,6 @@ +{{ template "_base.html" . }} {{ define "title" }}Distillery Control Page - Components Page{{ end }} -{{ define "head" }}{{ CSS }}{{ end }} -{{ define "footer" }}{{ JS }}{{ end }} - {{ define "header"}}

Control > diff --git a/internal/dis/component/control/info/html/info_index.html b/internal/dis/component/control/info/html/info_index.html index 3c256b0..cf1137b 100644 --- a/internal/dis/component/control/info/html/info_index.html +++ b/internal/dis/component/control/info/html/info_index.html @@ -1,8 +1,6 @@ +{{ template "_base.html" . }} {{ define "title" }}Distillery Control Page{{ end }} -{{ define "head" }}{{ CSS }}{{ end }} -{{ define "footer" }}{{ JS }}{{ end }} - {{ define "header"}}

Control diff --git a/internal/dis/component/control/info/html/info_instance.html b/internal/dis/component/control/info/html/info_instance.html index d933754..3675cd3 100644 --- a/internal/dis/component/control/info/html/info_instance.html +++ b/internal/dis/component/control/info/html/info_instance.html @@ -1,8 +1,6 @@ +{{ template "_base.html" . }} {{ define "title" }}Distillery Control Page - {{ .Info.Slug }}{{ end }} -{{ define "head" }}{{ CSS }}{{ end }} -{{ define "footer" }}{{ JS }}{{ end }} - {{ define "header"}}

Control > diff --git a/internal/dis/component/control/info/index.go b/internal/dis/component/control/info/index.go index 3d26739..8754300 100644 --- a/internal/dis/component/control/info/index.go +++ b/internal/dis/component/control/info/index.go @@ -15,8 +15,8 @@ import ( //go:embed "html/info_index.html" var indexTemplateStr string -var indexTemplate = static.AssetsControlIndex.MustParse( - base("info_index.html"), +var indexTemplate = static.AssetsControlIndex.MustParseShared( + "info_index.html", indexTemplateStr, ) diff --git a/internal/dis/component/control/info/instance.go b/internal/dis/component/control/info/instance.go index b37ee3b..c9c40dd 100644 --- a/internal/dis/component/control/info/instance.go +++ b/internal/dis/component/control/info/instance.go @@ -15,8 +15,8 @@ import ( //go:embed "html/info_instance.html" var instanceTemplateString string -var instanceTemplate = static.AssetsControlInstance.MustParse( - base("info_instance.html"), +var instanceTemplate = static.AssetsControlInstance.MustParseShared( + "info_instance.html", instanceTemplateString, ) diff --git a/internal/dis/component/control/static/assets.go b/internal/dis/component/control/static/assets.go index 025ad59..b6d2b38 100644 --- a/internal/dis/component/control/static/assets.go +++ b/internal/dis/component/control/static/assets.go @@ -1,7 +1,6 @@ package static import ( - "encoding/json" "html/template" ) @@ -25,26 +24,25 @@ type Assets struct { //go:generate node build.mjs HomeHome ComponentsIndex ControlIndex ControlInstance // MustParse parses a new template from the given source -// and registers the Asset functions to it. -// See [Assets.RegisterFuncs]. +// and calls [RegisterAssoc] on it. func (assets *Assets) MustParse(t *template.Template, value string) *template.Template { - if t == nil { - t = template.New("") - } - return template.Must(assets.RegisterFuncs(t).Parse(value)) + t = template.Must(t.Parse(value)) + assets.RegisterAssoc(t) + return t } -// RegisterFuncs registers three new template functions called "JS", "CSS" and "json". -// -// "JS" and "CSS" take no arguments, and return appropriate tags to be inserted into html. -// json takes a single argument of any type, and returns it's encoding as a string to be inserted into the page. -func (assets *Assets) RegisterFuncs(t *template.Template) *template.Template { - return t.Funcs(template.FuncMap{ - "JS": func() template.HTML { return template.HTML(assets.Scripts) }, - "CSS": func() template.HTML { return template.HTML(assets.Styles) }, - "json": func(data any) (string, error) { - bytes, err := json.Marshal(data) - return string(bytes), err - }, - }) +// MustParseShared is like [MustParse], but creates a new SharedTemplate instead +func (assets *Assets) MustParseShared(name string, value string) *template.Template { + return assets.MustParse(NewSharedTemplate(name), value) +} + +// RegisterAssoc registers two new associated templates with t. +// +// The template "scripts" will render all script tags required. +// The template "styles" will render all style tags required. +// +// If either template already exists, it will be overwritten. +func (assets *Assets) RegisterAssoc(t *template.Template) { + t.New("scripts").Parse(assets.Scripts) + t.New("styles").Parse(assets.Styles) } diff --git a/internal/dis/component/control/static/templates.go b/internal/dis/component/control/static/templates.go new file mode 100644 index 0000000..a318487 --- /dev/null +++ b/internal/dis/component/control/static/templates.go @@ -0,0 +1,30 @@ +package static + +import ( + "embed" + "encoding/json" + "html/template" +) + +//go:embed "templates/*.html" +var templates embed.FS + +var ( + shared *template.Template = template.Must(template.ParseFS(templates, "templates/*.html")) +) + +// NewSharedTemplate creates a new template with the given name. +// It will be able to make use of shared templates as well as functions. +func NewSharedTemplate(name string) *template.Template { + new := template.New(name) + new.Funcs(template.FuncMap{ + "json": func(data any) (string, error) { + bytes, err := json.Marshal(data) + return string(bytes), err + }, + }) + for _, template := range shared.Templates() { + new.AddParseTree(template.Tree.Name, template.Tree.Copy()) + } + return new +} diff --git a/internal/dis/component/control/info/html/base.html b/internal/dis/component/control/static/templates/_base.html similarity index 66% rename from internal/dis/component/control/info/html/base.html rename to internal/dis/component/control/static/templates/_base.html index 16c8b14..4faa834 100644 --- a/internal/dis/component/control/info/html/base.html +++ b/internal/dis/component/control/static/templates/_base.html @@ -3,14 +3,18 @@ - {{ block "title" . }}Distillery Control Page{{ end }} - {{ block "head" . }}head{{ end }} + {{ block "block" . }}Distillery Control Page{{ end }} + {{ block "styles" . }}styles{{ end }}

{{ template "title" . }}

+ {{ block "header/time" . }} + {{ if .Time }} Generated at {{ .Time.Format "2006-01-02T15:04:05Z07:00" }} + {{ end }} + {{ end }} {{ block "header" . }}header{{ end }}
@@ -19,8 +23,11 @@
+ {{ if .Time }} - {{ block "footer" . }}footer{{ end }} + {{ end }} + + {{ block "scripts" . }}scripts{{ end }} \ No newline at end of file diff --git a/pkg/httpx/html.go b/pkg/httpx/html.go index af8f580..4a017d8 100644 --- a/pkg/httpx/html.go +++ b/pkg/httpx/html.go @@ -6,8 +6,10 @@ import ( ) type HTMLHandler[T any] struct { - Handler func(r *http.Request) (T, error) - Template *template.Template // called with T + Handler func(r *http.Request) (T, error) + + Template *template.Template // called with T + TemplateName string // name of template to render, defaults to root } var htmlInternalServerErr = []byte(`Internal Server ErrorInternal Server Error`) @@ -37,5 +39,10 @@ func (h HTMLHandler[T]) ServeHTTP(w http.ResponseWriter, r *http.Request) { // write out the response as json w.Header().Set("Content-Type", "text/html") w.WriteHeader(http.StatusOK) - h.Template.Execute(w, result) + + if h.TemplateName != "" { + h.Template.ExecuteTemplate(w, h.TemplateName, result) + } else { + h.Template.Execute(w, result) + } }