From 7e9dc068fb6ec78ebd2a144579edd9f014abf961 Mon Sep 17 00:00:00 2001 From: Makis Maropoulos Date: Tue, 28 Jun 2016 18:06:52 +0300 Subject: [PATCH] Extend the Handlebars support as requested here https://github.com/kataras/iris/issues/239#issuecomment-229020737 History, Books & examples are updated. --- HISTORY.md | 18 +++++- context.go | 2 +- initiatory.go | 1 + .../template/engine/handlebars/handlebars.go | 56 ++++++++++++++++++- 4 files changed, 72 insertions(+), 5 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 04e64c09..72f7baf3 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,9 +1,23 @@ # History -## 3.0.0-rc.2 -> 3.0.0-rc.3 - **How to upgrade**: remove your $GOPATH/src/github.com/kataras folder, open your command-line and run `go get -u github.com/kataras/iris/iris`. +## 3.0.0-rc.3 -> 3.0.0-rc.4 + +**NEW**: **Handlebars** template engine support with all Iris' view engine's functions/helpers support: +- `iris.Config.Render.Template.Layout = "layouts/layout.html"` +- `config.NoLayout` +- **dynamic** optional layout on `context.Render` +- **Party specific** layout +- `iris.Config.Render.Template.Handlebars.Helpers["myhelper"] = func()...` +- `{{ yield }} ` +- `{{ render }}` +- `{{ url "myroute" myparams}}` +- `{{ urlpath "myroute" myparams}}` + +For a complete example please navigate [here](https://github.com/iris-contrib/examples/tree/master/templates_handlebars/templates). + +## 3.0.0-rc.2 -> 3.0.0-rc.3 **Breaking changes** - Move middleware & their configs to the [iris-contrib/middleware](https://github.com/iris-contrib/middleware) repository diff --git a/context.go b/context.go index 80f521c7..cbcb5224 100644 --- a/context.go +++ b/context.go @@ -476,7 +476,7 @@ func (ctx *Context) Render(name string, binding interface{}, layout ...string) e func (ctx *Context) MustRender(name string, binding interface{}, layout ...string) { if err := ctx.Render(name, binding, layout...); err != nil { ctx.Panic() - ctx.framework.Logger.Dangerf("MustRender panics for client with IP: %s On template: %s", ctx.RemoteAddr(), name) + ctx.framework.Logger.Dangerf("MustRender panics for client with IP: %s On template: %s.Trace: %s\n", ctx.RemoteAddr(), name, err) } } diff --git a/initiatory.go b/initiatory.go index 80d58495..56743fe3 100644 --- a/initiatory.go +++ b/initiatory.go @@ -38,6 +38,7 @@ func init() { const ( /* conversional */ + // HTMLEngine conversion for config.HTMLEngine HTMLEngine = config.HTMLEngine // PongoEngine conversion for config.PongoEngine diff --git a/render/template/engine/handlebars/handlebars.go b/render/template/engine/handlebars/handlebars.go index 5e86dee5..4acf91e2 100644 --- a/render/template/engine/handlebars/handlebars.go +++ b/render/template/engine/handlebars/handlebars.go @@ -39,6 +39,17 @@ func (e *Engine) BuildTemplates() error { if e.Config.Handlebars.Helpers != nil { raymond.RegisterHelpers(e.Config.Handlebars.Helpers) } + + // the render works like {{ render "myfile.html" theContext.PartialContext}} + // instead of the html/template engine which works like {{ render "myfile.html"}} and accepts the parent binding, with handlebars we can't do that because of lack of runtime helpers (dublicate error) + raymond.RegisterHelper("render", func(partial string, binding interface{}) raymond.SafeString { + contents, err := e.executeTemplateBuf(partial, binding) + if err != nil { + return raymond.SafeString("Template with name: " + partial + " couldn't not be found.") + } + return raymond.SafeString(contents) + }) + var templateErr error dir := e.Config.Directory @@ -84,6 +95,7 @@ func (e *Engine) BuildTemplates() error { } return nil }) + return templateErr } @@ -98,13 +110,53 @@ func (e *Engine) fromCache(relativeName string) *raymond.Template { return nil } +func (e *Engine) executeTemplateBuf(name string, binding interface{}) (string, error) { + if tmpl := e.fromCache(name); tmpl != nil { + return tmpl.Exec(binding) + } + return "", nil +} + // ExecuteWriter executes a templates and write its results to the out writer func (e *Engine) ExecuteWriter(out io.Writer, name string, binding interface{}, layout string) error { - if tmpl := e.fromCache(name); tmpl != nil { + + isLayout := false + + renderFilename := name + if layout != "" && layout != config.NoLayout { + isLayout = true + renderFilename = layout // the render becomes the layout, and the name is the partial. + } + + if tmpl := e.fromCache(renderFilename); tmpl != nil { + if isLayout { + var context map[string]interface{} + if m, is := binding.(map[string]interface{}); is { //handlebars accepts maps, + context = m + } else { + return fmt.Errorf("Please provide a map[string]interface{} type as the binding instead of the %#v", binding) + } + + contents, err := e.executeTemplateBuf(name, binding) + if err != nil { + return err + } + if context == nil { + context = make(map[string]interface{}, 1) + } + // I'm implemented the {{ yield }} as with the rest of template engines, so this is not inneed for iris, but the user can do that manually if want + // there is no performanrce different: raymond.RegisterPartialTemplate(name, tmpl) + context["yield"] = raymond.SafeString(contents) + } + res, err := tmpl.Exec(binding) + + if err != nil { + return err + } _, err = fmt.Fprint(out, res) return err } - return fmt.Errorf("[IRIS TEMPLATES] Template with name %s doesn't exists in the dir %s", name, e.Config.Directory) + return fmt.Errorf("[IRIS TEMPLATES] Template with name %s[original name = %s] doesn't exists in the dir %s", renderFilename, name, e.Config.Directory) }