Go template.ExecuteTemplate include html
Asked Answered
N

9

43

I have followed this tutorial: http://golang.org/doc/articles/wiki/final.go and have slightly modified it for my needs/wants. The problem is I would like to support HTML in the templates. I realize this is a security risk but it's not a concern at the moment.

The result of a page render:

<h1>this<strong>is</strong>a test</h1>

Let me explain a little bit of the code:

type Page struct {
    Title string
    Body  []byte
}

The data I would like to have HTML is stored in Page.Body. This is type []byte which means I can't (or can I?) run html/template.HTML(Page.Body) as that function expects a string.

I have this which pre-renders the templates:

var (
    templates = template.Must(template.ParseFiles("tmpl/edit.html", "tmpl/view.html"))
)

And the actual ExecuteTemplate looks like this:

err := templates.ExecuteTemplate(w, tmpl+".html", p)

Where w is w http.ResponseWriter, tmpl is tmpl string, and p is p *Page

Finally my 'view.html' (template) looks like the following:

<h1>{{.Title}}</h1>
<p>[<a href="/edit/{{.Title}}">edit</a>]</p>
<div>{{printf "%s" .Body}}</div>

Things I have tried:

  • {{printf "%s" .Body | html}} doesn't do anything
  • I have included github.com/russross/blackfriday (Markdown processor) and have run p.Body = blackfriday.MarkdownCommon(p.Body) which correctly converts Markdown to HTML, but the HTML is still output as entities.
  • EDIT: I have attempted the following bit of code (I don't know why the format is messed up) and it still outputs the exact same.

    var s template.HTML s = template.HTML(p.Body) p.Body = []byte(s)

Any guidance is greatly appreciated. If I'm being confusing please ask and I can modify my question.

Ney answered 11/8, 2013 at 19:4 Comment(0)
A
79

Convert your []byte or string to type template.HTML (documented here)

p.Body = template.HTML(s) // where s is a string or []byte

Then, in your template, just:

{{.Body}}

It will be printed without escaping.

EDIT

In order to be able to include HTML in you page's body you need to change the Page type declaration:

type Page struct {
    Title string
    Body  template.HTML
}

then assign to it.

Apprehension answered 12/8, 2013 at 11:22 Comment(5)
That doesn't work. I receive the following message: cannot use "html/template".HTML(p.Body) (type "html/template".HTML) as type []byte in assignmentNey
don't just copy-paste and say it doesn't work. you obviously need to change the type of your Body field to be able to assign something of type template.HTML to it.Apprehension
Please see the Edit I made last night to my question right after I posted the comment. I did make a change to the type of Body and I'm still seeing the exact same results.Ney
I added a function called 'noescape' that converts a string to template.HTML, then printf to it in a pipeline. Works magic.Topee
I spent a couple of days getting very angry at my template for working when I passed strings, but not when I passed html strings... Thanks!Abulia
B
22

Take a look at the template.HTML type. It can be used to encapsulate a known safe fragment of HTML (like the output from Markdown). The "html/template" package will not escape this this type.

type Page struct {
    Title string
    Body template.HTML
}

page := &Page{
    Title: "Example",
    Body:  template.HTML(blackfriday.MarkdownCommon([]byte("foo bar")),
}

I usually write my own func Markdown(text string) html.Template method that calls blackfriday with the appropriate config and does some type conversions. Another alternative might be also to register a "html" func in the template parser, that allows you to output any value without any escaping by doing something like {{html .MySafeStr}}. The code might look like:

var tmpl = template.Must(template.New("").Funcs(template.FuncMap{
    "html": func(value interface{}) template.HTML {
        return template.HTML(fmt.Sprint(value))
    },
}).ParseFiles("file1.html", "file2.html"))
Bonine answered 15/8, 2013 at 10:5 Comment(0)
C
21

I created a custom function for the template as follows:

func noescape(str string) template.HTML {
    return template.HTML(str)
}

var fn = template.FuncMap{
    "noescape": noescape,
}

Then on your template:

{{ noescape $x.Body }}
Cule answered 5/2, 2017 at 17:40 Comment(0)
H
3

Here's an approach that doesn't require any changes to your existing structs, and a very minimal, additive change to your templates:

Change these lines:

var (
    templates = template.Must(template.ParseFiles("tmpl/edit.html", "tmpl/view.html"))
)

to this (include a funcmap with a function that will output un-escaped HTML):

var templates = template.Must(template.New("main").Funcs(template.FuncMap{
    "safeHTML": func(b []byte) template.HTML {
        return template.HTML(b)
    },
}).ParseFiles("tmpl/edit.html", "tmpl/view.html"))

And then just change your template HTML from this:

<div>{{printf "%s" .Body}}</div>

to this (use your new function):

<div>{{ .Body | safeHTML }}</div>

Much easier!

Haler answered 27/5, 2017 at 22:36 Comment(0)
A
1

I'm using Beego and React.js and fought for hours trying to get the JSX parser to run. Turns out html/template strips out comments especially the js doc block /** @jsx React.DOM */.

Got around it by creating a special method to Type the comment as JS and calling it from within the template.

// Create a method in your controller (I'm using Beego)
func jsxdoc()(out template.JS) {
    return template.JS(`/** @jsx React.DOM */`)
}

// Add method to your function map available to views
beego.AddFuncMap("jsxdoc", jsxdoc)

// In template
<script type="text/jsx">
    {{ jsxdoc }}
    var CommentBox = React.createClass({
      render: function() {
        return (
          <div class="commentBox">
            Hello, world! I am a CommentBox.
          </div>
        );
      }
    });
    React.renderComponent(
      <CommentBox />,
      document.getElementById('content')
    );
</script>
Audiophile answered 22/7, 2014 at 2:3 Comment(0)
F
0

For clarification and a much simpler way of passing HTML to template, see

https://groups.google.com/forum/#!topic/golang-nuts/8L4eDkr5Q84

Just create your HTML string via go and pass it into your template, e.g.:

Sout := ""
.
.

    Sout += fmt.Sprintf(`<tr><td>%s<td align=center>%.2f<td>%s<td>%s<td>%s<td>%s<td align=center>%d<td align=center>%d
                    <td align=center>%d`, AccountID, amount, remissiondetails, created, begins, ends,
                    freePDFs, freeinformants, freeSDQs)

.
.
    render(w, "templates/Waivers.html", map[string]interface{}{ "Body":template.HTML(Sout), })
Fairfax answered 13/1, 2017 at 11:13 Comment(0)
S
0

In my case (where I am populating a view struct with a list of Activity), I had to change the property Message string to Message template.HTML. Then, when setting the property value I can call activity.Message = template.HTML("The <b>HTML</b>").

Sharpset answered 2/11, 2019 at 18:25 Comment(0)
V
0

I'm here thanks to the same tutorial.

Tried almost all the solutions above but the one that just changes the type of Page.Body.

Didn't want to permanently change the type of Body, suspecting, out of my ignorance, that the original []byte type may be needed for file IO.

Since the mission was to render each [page] as <a href="/page">page</a> I decided to add a member HTMLBody of type template.HTML to the Page struct, update that member in renderTemplate before the template execution, and render it as {{.HTMLBody}}.

This is my renderTemplate function:

func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
    body := string(p.Body)
    p.HTMLBody = template.HTML(pageLink.ReplaceAllString(body, "<a href='/view/$1'>$1</a>"))
    err := templates.ExecuteTemplate(w, tmpl+".html", p)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
}

Hope this may help.

Villainy answered 2/6 at 8:27 Comment(0)
L
-1

Why not convert the []byte to a string? You can do it like this:

str := string(page.Body)
Lurleen answered 11/8, 2013 at 19:35 Comment(1)
I tried that, the issue is template.ExecuteTemplate needs a pointer to Page which requires it to be a []byte I tried doing exactly what you mentioned and received the following error: cannot use string(p.Body) (type string) as type []byte in assignment . Likewise changing my struct to use a string instead of a []byte caused other issues when saving the content to file.Ney

© 2022 - 2024 — McMap. All rights reserved.