Skip to content

Commit

Permalink
Added optional data map to contentFor/contentOf helpers (gobuffalo#55)
Browse files Browse the repository at this point in the history
* Added optional data map to contentFor/contentOf helpers

addresses gobuffalo#54

* added example to README

* better docs

* explained the map is optional

* ensured map doesn't affect outer context

* added better comment

* better implementation of creating sub-block

* defensive programming to avoid panic

This is a freebee :)
  • Loading branch information
matryer authored and markbates committed Jun 9, 2018
1 parent 3072d52 commit b0b0a05
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 15 deletions.
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,35 @@ fmt.Print(s)
* `form` - support for the [github.com/gobuffalo/tags/form](https://github.com/gobuffalo/tags/tree/master/form) package (Bootstrap version)
* `form_for` - support for the [github.com/gobuffalo/tags/form](https://github.com/gobuffalo/tags/tree/master/form) package (Bootstrap version) to build a form for a model

#### contentFor and contentOf

Use the `contentFor` and `contentOf` helpers to dry up your templates with reusable components.

For example, we can define a snippet that generates a fancy title using `contentFor`:

```
<% contentFor("fancy-title") { %>
<h1 class='fancy'><%= title %></h1>
<% } %>
```

The `fancy-title` name is how we will invoke this with `contentOf` elsewhere
in our template:

```
<%= contentOf("fancy-title", {"title":"Welcome to Plush"}) %>
```

* The second map argument is optional, for static content just use `<%= contentOf("fancy-title") %>`

Rendering this would generate this output:

```
<h1 class='fancy'>Welcome to Plush</h1>
```

As you can see, the `<%= title %>` has been replaced with the `Welcome to Plush` string.

#### truncate

`truncate` takes two optional parameters:
Expand Down
9 changes: 8 additions & 1 deletion ast/assign_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,12 @@ type AssignExpression struct {
func (ae *AssignExpression) expressionNode() {}

func (ae *AssignExpression) String() string {
return fmt.Sprintf("%s = %s", ae.Name.String(), ae.Value.String())
n, v := "?", "?"
if ae.Name != nil {
n = ae.Name.String()
}
if ae.Value != nil {
v = ae.Value.String()
}
return fmt.Sprintf("%s = %s", n, v)
}
35 changes: 21 additions & 14 deletions content_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,36 @@ import (
"github.com/pkg/errors"
)

// ContentFor stores a block of templating code to be re-used later in the template.
// ContentFor stores a block of templating code to be re-used later in the template
// via the contentOf helper.
// An optional map of values can be passed to contentOf,
// which are made available to the contentFor block.
/*
<% contentFor("buttons") { %>
<button>hi</button>
<% } %>
*/
func contentForHelper(name string, help HelperContext) (template.HTML, error) {
body, err := help.Block()
if err != nil {
return "", errors.WithStack(err)
}
b := template.HTML(body)
help.Set(name, b)
return b, nil
func contentForHelper(name string, help HelperContext) {
help.Set("contentFor:"+name, func(data map[string]interface{}) (template.HTML, error) {
ctx := help.New()
for k, v := range data {
ctx.Set(k, v)
}
body, err := help.BlockWith(ctx)
if err != nil {
return "", errors.WithStack(err)
}
return template.HTML(body), nil
})
}

// ContentOf retrieves a stored block for templating and renders it.
// You can pass an optional map of fields that will be set.
/*
<%= contentOf("buttons") %>
<%= contentOf("buttons", {"label": "Click me"}) %>
*/
func contentOfHelper(name string, help HelperContext) template.HTML {
if s := help.Value(name); s != nil {
return s.(template.HTML)
}
return ""
func contentOfHelper(name string, data map[string]interface{}, help HelperContext) (template.HTML, error) {
fn := help.Value("contentFor:" + name).(func(data map[string]interface{}) (template.HTML, error))
return fn(data)
}
18 changes: 18 additions & 0 deletions content_helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,21 @@ func Test_ContentForOf(t *testing.T) {
r.Contains(s, "<b1><button>hi</button></b1>")
r.Contains(s, "<b2><button>hi</button></b2>")
}

func Test_ContentForOfWithData(t *testing.T) {
r := require.New(t)
input := `
<b0><% contentFor("buttons") { %><button><%= label %></button><% } %></b0>
<b1><%= contentOf("buttons", {"label": "Button One"}) %></b1>
<b2><%= contentOf("buttons", {"label": "Button Two"}) %></b2>
<b3><%= label %></b3>
`
ctx := NewContext()
ctx.Set("label", "Outer label")
s, err := Render(input, ctx)
r.NoError(err)
r.Contains(s, "<b0></b0>")
r.Contains(s, "<b1><button>Button One</button></b1>")
r.Contains(s, "<b2><button>Button Two</button></b2>")
r.Contains(s, "<b3>Outer label</b3>", "the outer label shouldn't be affected by the map passed in")
}

0 comments on commit b0b0a05

Please sign in to comment.