'Assign Golang variable to Javascript

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?



Solution 1:[1]

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.

Solution 2:[2]

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>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 joeyave