|
| 1 | +--- |
| 2 | +title: Partial decorators |
| 3 | +description: Use partial decorators to create reusable wrapper components that enclose and compose template content. |
| 4 | +categories: [] |
| 5 | +keywords: [decorator] |
| 6 | +weight: 170 |
| 7 | +--- |
| 8 | + |
| 9 | +## Overview |
| 10 | + |
| 11 | +{{% glossary-term "partial decorator" %}} |
| 12 | + |
| 13 | +This approach creates a connection between two files. The calling template provides a block of code and the partial decorator determines where that code appears. This allows the partial to wrap around content without needing to know the specific markup or internal logic of the enclosed block. |
| 14 | + |
| 15 | +## Implementation |
| 16 | + |
| 17 | +To use a partial decorator, use a block-style call in your templates. The [`with`][] statement is required to initiate the partial and create a container for the content. This block can include any valid template code including page methods and functions. |
| 18 | + |
| 19 | +```go-html-template {file="layouts/home.html" copy=true} |
| 20 | +{{ with partial "components/wrapper.html" . }} |
| 21 | + <p>Everything in this block will be wrapped.</p> |
| 22 | + <p>{{ .Content | transform.Plainify | strings.Truncate 200 }}</p> |
| 23 | +{{ end }} |
| 24 | +``` |
| 25 | + |
| 26 | +Inside the partial template, place the `templates.Inner` function call where the wrapped content should appear. |
| 27 | + |
| 28 | +```go-html-template {file="layouts/_partials/components/wrapper.html" copy=true} |
| 29 | +<div class="wrapper-styling"> |
| 30 | + {{ templates.Inner . }} |
| 31 | +</div> |
| 32 | +``` |
| 33 | + |
| 34 | +The `with` statement creates a new [scope](g). Variables defined outside of the `with` block are not available inside it. To use external data within the wrapped content, you must ensure it is part of the [context](g) passed in the partial call. |
| 35 | + |
| 36 | +A key feature of the `templates.Inner` function is its ability to accept a context argument. By passing a context to the function, you define what the dot (`.`) represents inside the wrapped block. This ensures that the injected content has access to the correct data even when nested inside multiple layers of wrappers. |
| 37 | + |
| 38 | +## Benefits of composition |
| 39 | + |
| 40 | +Using partial decorators to build wrapper components provides several advantages: |
| 41 | + |
| 42 | +- It eliminates the need to use separate partials for opening and closing tags when encapsulating a block of code. |
| 43 | +- It prevents parameter bloat because a standard partial no longer requires an extensive list of arguments to account for every possible variation of the content inside it. |
| 44 | +- It enables clean composition where the wrapped block can execute any template logic without the wrapper needing to receive or process that data. |
| 45 | + |
| 46 | +This approach separates container logic from content logic. The wrapper handles structural requirements like specific class hierarchies or CSS grid containers. The calling template retains control over the inner markup and how data is displayed. |
| 47 | + |
| 48 | +## Example |
| 49 | + |
| 50 | +The following templates illustrate how to nest three wrapper components including a section, a column, and a card while passing context through each layer. |
| 51 | + |
| 52 | +The home template initiates the structure by calling the section, column, and card partials as decorators: |
| 53 | + |
| 54 | +```go-html-template {file="layouts/home.html" copy=true} |
| 55 | +{{ $ctx := dict |
| 56 | + "page" . |
| 57 | + "label" "Recent Posts" |
| 58 | + "pageCollection" ((site.GetPage "/posts").RegularPages) |
| 59 | +}} |
| 60 | +
|
| 61 | +{{ with partial "components/section.html" $ctx }} |
| 62 | + <div class="grid-wrapper"> |
| 63 | + {{ range .pageCollection }} |
| 64 | + {{ with partial "components/column.html" (dict "page" . "class" "col-half") }} |
| 65 | + {{ with partial "components/card.html" (dict "page" .page "url" .page.RelPermalink "title" .page.LinkTitle) }} |
| 66 | + <p> |
| 67 | + {{ .page.Content | plainify | strings.Truncate 240 }} |
| 68 | + </p> |
| 69 | + {{ end }} |
| 70 | + {{ end }} |
| 71 | + {{ end }} |
| 72 | + </div> |
| 73 | +{{ end }} |
| 74 | +``` |
| 75 | + |
| 76 | +The section component provides a semantic container and an optional heading: |
| 77 | + |
| 78 | +```go-html-template {file="layouts/_partials/components/section.html" copy=true} |
| 79 | +<section class="content-section"> |
| 80 | + {{ with .label }} |
| 81 | + <h2 class="section-label">{{ . }}</h2> |
| 82 | + {{ end }} |
| 83 | + <div class="section-content"> |
| 84 | + {{ templates.Inner . }} |
| 85 | + </div> |
| 86 | +</section> |
| 87 | +``` |
| 88 | + |
| 89 | +The column component manages layout width by applying a CSS class: |
| 90 | + |
| 91 | +```go-html-template {file="layouts/_partials/components/column.html" copy=true} |
| 92 | +<div class="{{ .class | default `column-default` }}"> |
| 93 | + {{ templates.Inner . }} |
| 94 | +</div> |
| 95 | +``` |
| 96 | + |
| 97 | +The card component defines the visual boundary for the content: |
| 98 | + |
| 99 | +```go-html-template {file="layouts/_partials/components/card.html" copy=true} |
| 100 | +<div class="card"> |
| 101 | + {{ with .title }} |
| 102 | + <h2 class="card-title"> |
| 103 | + {{ if $.url }} |
| 104 | + <a href="{{ $.url }}">{{ . }}</a> |
| 105 | + {{ else }} |
| 106 | + {{ . }} |
| 107 | + {{ end }} |
| 108 | + </h2> |
| 109 | + {{ end }} |
| 110 | +
|
| 111 | + <div class="card-body"> |
| 112 | + {{ templates.Inner . }} |
| 113 | + </div> |
| 114 | +
|
| 115 | + {{ with .url }} |
| 116 | + <div class="card-footer"> |
| 117 | + <a href="{{ . }}">Read more</a> |
| 118 | + </div> |
| 119 | + {{ end }} |
| 120 | +</div> |
| 121 | +``` |
| 122 | + |
| 123 | +[`with`]: /functions/go-template/with/ |
0 commit comments