7

Currently I am having an issue related to assign a Golang variable to a Javascript variable. I am using the Golang templates, so, from the backend I sent a JSON variable, just like this:

 var c []models.C
 b, _ := json.Marshal(c)
 err = tpl.ExecuteTemplate(w, "index.gohtml",string(b))

As you see, I have a slice, convert it to Json and then that Json to string, and send it to the template. Then, in the frontend I need to assign that to a variable, and it should be valid JSON, I have this:

var rowData = {{.}};

But, I am getting SyntaxError: expected property name, got '{'

So, my question is: How should I assign that JSON?

icza
  • 389,944
  • 63
  • 907
  • 827
Sredny M Casanova
  • 4,735
  • 21
  • 70
  • 115
  • 2
    I'm not sure what you want to achive, but this `{{.}}` doesn't seem like a JSON -> https://jsonlint.com/ – Edwin Aug 03 '17 at 15:24
  • 1
    Please show the complete example. "SyntaxError: expected property name" isn't a Go error. What is the output of your template? Is this a text template? Why not just `fmt.Sprintf("var rowData = %s;", b)`? – JimB Aug 03 '17 at 15:25
  • 1
    @Edwin with {{.}} I print the Golang variable in the frontend. {{.}} it's not the value, it's a way to access the variable – Sredny M Casanova Aug 03 '17 at 15:26
  • 1
    @JimB I agree, it's a Javascript issue, because I am trying to assign the Go variable (it's a string but JSON valid) to the JS variable – Sredny M Casanova Aug 03 '17 at 15:28
  • @SrednyMCasanova without the code to show where that is used, it is hard to tell that this was used in a template or just the output of the template. – Edwin Aug 04 '17 at 07:46

2 Answers2

10

First, you must use the html/template instead of text/template, as the former provides context-sensitive escaping.

Second, in the template the context must be clear that it is JavaScript code, e.g. it must be inside HTML <script> tag.

See this working example:

type Point struct {
    Name string
    X, Y int
}

func main() {
    t := template.Must(template.New("").Parse(src))

    p := Point{"Center", 100, 200}
    pj, err := json.Marshal(p)
    if err != nil {
        panic(err)
    }

    if err = t.Execute(os.Stdout, string(pj)); err != nil {
        panic(err)
    }
}

const src = `<script>
var point = {{.}};
alert(point);
</script>`

Output (try it on the Go Playground):

<script>
var point = "{\"Name\":\"Center\",\"X\":100,\"Y\":200}";
alert(point);
</script>

As you can see, the point JavaScript variable contains a valid JSON text (a JavaScript Object), properly escaped.

icza
  • 389,944
  • 63
  • 907
  • 827
1

You can write custom template function that will marshal your struct into json. That way you don't have to marshal all your structs in the handler yourself. Especially if you need your struct in HTML template as well as in JS script.

Example using Gin.

In main.go file.

    router := gin.New()
    router.SetFuncMap(template.FuncMap{
        "json": func(s interface{}) string {
            jsonBytes, err := json.Marshal(s)
            if err != nil {
                return ""
            }
            return string(jsonBytes)
        },
    })

In your handler function.

type MyStruct struct {
    Foo string `json:"foo"`
}

func Handler(ctx *gin.Context) {
    myStruct := MyStruct{
        Foo: "test",
    }
    
    ctx.HTML(http.StatusOK, "template-file.go.html", gin.H{
        "MyStruct":   myStruct,
    })
}

Finally in your template.go.html file add a script tag and define here your global variables. Later on you can access them in your JS file.

<script type="text/javascript">
    // NB on JSON.parse and json functions!
    let event = JSON.parse({{ json .MyStruct }});
</script>

// Below you can import your JS file as usual.
<script type="text/javascript" src="/assets/js/index.js"></script>
joeyave
  • 316
  • 3
  • 11