Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ feature: bind support for render #1754

Merged
merged 13 commits into from Feb 12, 2022
1 change: 1 addition & 0 deletions .github/testdata/template2.html
@@ -0,0 +1 @@
<h1>{{.Title}} {{.Summary}}</h1>
65 changes: 46 additions & 19 deletions ctx.go
Expand Up @@ -62,6 +62,7 @@ type Ctx struct {
values [maxParams]string // Route parameter values
fasthttp *fasthttp.RequestCtx // Reference to *fasthttp.RequestCtx
matched bool // Non use route matched
viewBindMap Map // Default view map to bind template engine
}

// Range data for c.Range
Expand Down Expand Up @@ -126,6 +127,8 @@ func (app *App) AcquireCtx(fctx *fasthttp.RequestCtx) *Ctx {
c.fasthttp = fctx
// reset base uri
c.baseURI = ""
// init viewBindMap
c.viewBindMap = make(Map)
ReneWerner87 marked this conversation as resolved.
Show resolved Hide resolved
ReneWerner87 marked this conversation as resolved.
Show resolved Hide resolved
// Prettify path
c.configDependentPaths()
return c
Expand Down Expand Up @@ -1054,6 +1057,16 @@ func (c *Ctx) Redirect(location string, status ...int) error {
return nil
}

// Add vars to defult view var map binding to template engine.
// Variables are using by Render method and they can be overwritten.
efectn marked this conversation as resolved.
Show resolved Hide resolved
func (c *Ctx) Bind(vars Map) error {
for k, v := range vars {
c.viewBindMap[k] = v
}

return nil
}

// Render a template with data and sends a text/html response.
// We support the following engines: html, amber, handlebars, mustache, pug
func (c *Ctx) Render(name string, bind interface{}, layouts ...string) error {
Expand All @@ -1062,25 +1075,8 @@ func (c *Ctx) Render(name string, bind interface{}, layouts ...string) error {
buf := bytebufferpool.Get()
defer bytebufferpool.Put(buf)

// Check if the PassLocalsToViews option is enabled (By default it is disabled)
if c.app.config.PassLocalsToViews {
// Safely cast the bind interface to a map
bindMap, ok := bind.(Map)
// Check if the bind is a map
if ok {
// Loop through each local and set it in the map
c.fasthttp.VisitUserValues(func(key []byte, val interface{}) {
// check if bindMap doesn't contain the key
if _, ok := bindMap[string(key)]; !ok {
// Set the key and value in the bindMap
bindMap[string(key)] = val
}
})
// set the original bind to the map
bind = bindMap
}

}
// Pass-locals-to-views & Bind
c.renderExtensions(bind)

if c.app.config.Views != nil {
// Render template based on global layout if exists
Expand Down Expand Up @@ -1117,6 +1113,37 @@ func (c *Ctx) Render(name string, bind interface{}, layouts ...string) error {
return err
}

func (c *Ctx) renderExtensions(bind interface{}) {
ReneWerner87 marked this conversation as resolved.
Show resolved Hide resolved
// Bind view map
if bindMap, ok := bind.(Map); ok {
for k, v := range c.viewBindMap {
bindMap[k] = v
}

// set the original bind to the map
bind = bindMap
}

// Check if the PassLocalsToViews option is enabled (By default it is disabled)
efectn marked this conversation as resolved.
Show resolved Hide resolved
if c.app.config.PassLocalsToViews {
// Safely cast the bind interface to a map
bindMap, ok := bind.(Map)
// Check if the bind is a map
if ok {
// Loop through each local and set it in the map
c.fasthttp.VisitUserValues(func(key []byte, val interface{}) {
// check if bindMap doesn't contain the key
if _, ok := bindMap[string(key)]; !ok {
// Set the key and value in the bindMap
bindMap[string(key)] = val
}
})
// set the original bind to the map
bind = bindMap
}
}
}

// Route returns the matched Route struct.
func (c *Ctx) Route() *Route {
if c.route == nil {
Expand Down
49 changes: 49 additions & 0 deletions ctx_test.go
Expand Up @@ -2087,6 +2087,55 @@ func Test_Ctx_RenderWithLocals(t *testing.T) {
utils.AssertEqual(t, "<h1>Hello, World!</h1>", string(c.Response().Body()))

}

func Test_Ctx_RenderWithBind(t *testing.T) {
t.Parallel()

app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})

c.Bind(Map{
"Title": "Hello, World!",
})
defer app.ReleaseCtx(c)
err := c.Render("./.github/testdata/template.html", Map{})

buf := bytebufferpool.Get()
_, _ = buf.WriteString("overwrite")
defer bytebufferpool.Put(buf)

utils.AssertEqual(t, nil, err)
utils.AssertEqual(t, "<h1>Hello, World!</h1>", string(c.Response().Body()))

}

func Test_Ctx_RenderWithBindLocals(t *testing.T) {
t.Parallel()

app := New(Config{
PassLocalsToViews: true,
})

c := app.AcquireCtx(&fasthttp.RequestCtx{})

c.Bind(Map{
"Title": "Hello, World!",
})

c.Locals("Summary", "Test")

defer app.ReleaseCtx(c)
err := c.Render("./.github/testdata/template2.html", Map{})

buf := bytebufferpool.Get()
_, _ = buf.WriteString("overwrite")
defer bytebufferpool.Put(buf)

utils.AssertEqual(t, nil, err)
utils.AssertEqual(t, "<h1>Hello, World! Test</h1>", string(c.Response().Body()))

}

func Test_Ctx_RenderWithLocalsAndBinding(t *testing.T) {
t.Parallel()
app := New(Config{
Expand Down