Skip to content

Commit

Permalink
feat: add support for rendering raw HTML, and bi-directional support …
Browse files Browse the repository at this point in the history
…for html/template (#337)

Fixes #175
  • Loading branch information
a-h authored Dec 21, 2023
1 parent 6a3dc32 commit 8484597
Show file tree
Hide file tree
Showing 18 changed files with 540 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.2.499
0.2.500
89 changes: 89 additions & 0 deletions docs/docs/03-syntax-and-usage/14-using-with-go-templates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Using with `html/template`

Templ components can be used with the Go standard library [`html/template`](https://pkg.go.dev/html/template) package.

## Using `html/template` in a templ component

To use an existing `html/template` in a templ component, use the `templ.FromGoHTML` function.

```templ title="component.templ"
package testgotemplates
import "html/template"
var goTemplate = template.Must(template.New("example").Parse("<div>{{ . }}</div>"))
templ Example() {
<!DOCTYPE html>
<html>
<body>
@templ.FromGoHTML(goTemplate, "Hello, World!")
</body>
</html>
}
```

```go title="main.go"
func main() {
Example.Render(context.Background(), os.Stdout)
}
```

```html title="Output"
<!DOCTYPE html>
<html>
<body>
<div>Hello, World!</div>
</body>
</html>
```

## Using a templ component with `html/template`

To use a templ component within a `html/template`, use the `templ.ToGoHTML` function to render the component into a `template.HTML value`.

```templ title="component.html"
package testgotemplates
import "html/template"
var example = template.Must(template.New("example").Parse(`<!DOCTYPE html>
<html>
<body>
{{ . }}
</body>
</html>
`))
templ greeting() {
<div>Hello, World!</div>
}
```

```go title="main.go"
func main() {
// Create the templ component.
templComponent := greeting()

// Render the templ component to a `template.HTML` value.
html, err := templ.ToGoHTML(context.Background(), templComponent)
if err != nil {
t.Fatalf("failed to convert to html: %v", err)
}

// Use the `template.HTML` value within the text/html template.
err = example.Execute(os.Stdout, html)
if err != nil {
t.Fatalf("failed to execute template: %v", err)
}
}
```

```html title="Output"
<!DOCTYPE html>
<html>
<body>
<div>Hello, World!</div>
</body>
</html>
```
31 changes: 31 additions & 0 deletions docs/docs/03-syntax-and-usage/15-rendering-raw-html.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Rendering raw HTML

To render HTML that has come from a trusted source, bypassing all HTML escaping and security mechanisms that templ includes, use the `templ.Raw` function.

:::info
Only include HTML that comes from a trusted source.
:::

:::warning
Use of this function may introduce security vulnerabilities to your program.
:::

```templ title="component.templ"
templ Example() {
<!DOCTYPE html>
<html>
<body>
@templ.Raw("<div>Hello, World!</div>")
</body>
</html>
}
```

```html title="Output"
<!DOCTYPE html>
<html>
<body>
<div>Hello, World!</div>
</body>
</html>
```
4 changes: 4 additions & 0 deletions docs/docs/12-integrations/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,7 @@ https://github.com/a-h/templ/tree/main/examples/integration-gin
An example of using templ with go-chi v5 can be found here:

https://github.com/a-h/templ/tree/main/examples/integration-chi

## `template/html`

See [Using with Go templates](../syntax-and-usage/using-with-go-templates)
35 changes: 35 additions & 0 deletions generator/htmldiff/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,41 @@ import (
"github.com/google/go-cmp/cmp"
)

func DiffStrings(expected, actual string) (diff string, err error) {
// Format both strings.
var wg sync.WaitGroup
wg.Add(2)

var errs []error

// Format expected.
go func() {
defer wg.Done()
e := new(strings.Builder)
err := htmlformat.Fragment(e, strings.NewReader(expected))
if err != nil {
errs = append(errs, fmt.Errorf("expected html formatting error: %w", err))
}
expected = e.String()
}()

// Format actual.
go func() {
defer wg.Done()
a := new(strings.Builder)
err := htmlformat.Fragment(a, strings.NewReader(actual))
if err != nil {
errs = append(errs, fmt.Errorf("actual html formatting error: %w", err))
}
actual = a.String()
}()

// Wait for processing.
wg.Wait()

return cmp.Diff(expected, actual), errors.Join(errs...)
}

func Diff(input templ.Component, expected string) (diff string, err error) {
return DiffCtx(context.Background(), input, expected)
}
Expand Down
6 changes: 6 additions & 0 deletions generator/test-go-template-in-templ/expected.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<!DOCTYPE html>
<html>
<body>
<div>Hello, World!</div>
</body>
</html>
22 changes: 22 additions & 0 deletions generator/test-go-template-in-templ/render_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package testgotemplates

import (
_ "embed"
"testing"

"github.com/a-h/templ/generator/htmldiff"
)

//go:embed expected.html
var expected string

func TestExample(t *testing.T) {
component := Example()
diff, err := htmldiff.Diff(component, expected)
if err != nil {
t.Fatal(err)
}
if diff != "" {
t.Error(diff)
}
}
14 changes: 14 additions & 0 deletions generator/test-go-template-in-templ/template.templ
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package testgotemplates

import "html/template"

var goTemplate = template.Must(template.New("example").Parse("<div>{{ . }}</div>"))

templ Example() {
<!DOCTYPE html>
<html>
<body>
@templ.FromGoHTML(goTemplate, "Hello, World!")
</body>
</html>
}
46 changes: 46 additions & 0 deletions generator/test-go-template-in-templ/template_templ.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions generator/test-raw-elements/expected.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@
}
</script>
<h1>Hello</h1>
<div>World</div>
</body>
</html>
1 change: 1 addition & 0 deletions generator/test-raw-elements/template.templ
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ templ Example() {
}
</script>
<h1>Hello</h1>
@templ.Raw("<div>World</div>")
</body>
</html>
}
10 changes: 9 additions & 1 deletion generator/test-raw-elements/template_templ.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions generator/test-templ-in-go-template/expected.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<!DOCTYPE html>
<html>
<body>
<div>Hello, World!</div>
</body>
</html>
39 changes: 39 additions & 0 deletions generator/test-templ-in-go-template/render_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package testgotemplates

import (
"context"
_ "embed"
"strings"
"testing"

"github.com/a-h/templ"
"github.com/a-h/templ/generator/htmldiff"
)

//go:embed expected.html
var expected string

func TestExample(t *testing.T) {
// Create the templ component.
templComponent := greeting()
html, err := templ.ToGoHTML(context.Background(), templComponent)
if err != nil {
t.Fatalf("failed to convert to html: %v", err)
}

// Use it within the text/html template.
b := new(strings.Builder)
err = example.Execute(b, html)
if err != nil {
t.Fatalf("failed to execute template: %v", err)
}

// Compare the output with the expected.
diff, err := htmldiff.DiffStrings(expected, b.String())
if err != nil {
t.Fatalf("failed to diff strings: %v", err)
}
if diff != "" {
t.Error(diff)
}
}
15 changes: 15 additions & 0 deletions generator/test-templ-in-go-template/template.templ
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package testgotemplates

import "html/template"

var example = template.Must(template.New("example").Parse(`<!DOCTYPE html>
<html>
<body>
{{ . }}
</body>
</html>
`))

templ greeting() {
<div>Hello, World!</div>
}
Loading

0 comments on commit 8484597

Please sign in to comment.