The Go code for the chart part
The basic of charts in Go is the possibility to add a function into Go HTML template. Similar to Javascript in HTML onclick="myFunction()". The difference is that this custom template function will be reachable from all templates.
1. The chart() function
This is the "func" that is called from the Go template.
func chart(path string) string { rest := strings.Split(path, "/") module := rest[0] mode := rest[1] val := rest[2] sfx := rest[3] //suffix to address separate charts json_data := get_data(module, mode, val) switch mode { case "bar": return get_bar(json_data, sfx) case "pie": return get_pie(json_data, sfx) case "line": return get_line(json_data, sfx) } return "bar" }
2. Add chart() function to all Go templates
I prefer to do this once in an init() function
func init() { tpl = template.Must(template.New("").Funcs(template.FuncMap{ "chart": chart, //add the chart function }).ParseGlob("./public/tmpl/*/*.html")) //two levels deep }
3. The Go chart code
I have separated all chart functions in a separate Go file.
package main import ( "strings" ) func chart(path string) string { rest := strings.Split(path, "/") module := rest[0] mode := rest[1] val := rest[2] sfx := rest[3] //to address separate charts json_data := get_data(module, mode, val) switch mode { case "bar": return get_bar(json_data, sfx) case "pie": return get_pie(json_data, sfx) case "line": return get_line(json_data, sfx) } return "bar" } func get_data(module, mode, val string) string { return `{ "Active":"25", "On hold":"20", "Draft":"30" }` } func get_bar(json_data, sfx string) string { return ` var ctx` + sfx + ` = document.getElementById('chrt` + sfx + `').getContext('2d'); var jsonData` + sfx + ` = ` + json_data + `; var labels` + sfx + ` = Object.keys(jsonData` + sfx + `); var data` + sfx + ` = Object.values(jsonData` + sfx + `).map(Number); var myChart` + sfx + ` = new Chart(ctx` + sfx + `, { type: 'bar', data: { labels: labels` + sfx + `, datasets: [{ label: '# of Votes', data: data` + sfx + `, backgroundColor: [ 'rgba(255, 99, 132, 10)', 'rgba(54, 162, 235, 10)', 'rgba(255, 206, 86, 10)', 'rgba(75, 192, 192, 10)', 'rgba(153, 102, 255, 10)', 'rgba(255, 159, 64, 10)' ], borderWidth: 1 }] }, options: { scales: { y: { beginAtZero: true } }, plugins: { title: { display: true, text: 'Bar Chart Title', font: { size: 20, family: 'Source Sans Pro', } } } } }); ` } func get_pie(json_data, sfx string) string { return ` var ctx` + sfx + ` = document.getElementById('chrt` + sfx + `').getContext('2d'); var jsonData` + sfx + ` = ` + json_data + `; var labels` + sfx + ` = Object.keys(jsonData` + sfx + `); var data` + sfx + ` = Object.values(jsonData` + sfx + `).map(Number); var myChart` + sfx + ` = new Chart(ctx` + sfx + `, { type: 'pie', data: { labels: labels` + sfx + `, datasets: [{ label: '# of Votes', data: data` + sfx + `, backgroundColor: [ 'rgba(255, 99, 132, 10)', 'rgba(54, 162, 235, 10)', 'rgba(255, 206, 86, 10)', 'rgba(75, 192, 192, 10)', 'rgba(153, 102, 255, 10)', 'rgba(255, 159, 64, 10)' ], }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'top', }, title: { display: true, text: 'Pie Chart Title', font: { size: 20, family: 'Source Sans Pro', } } } }, }); ` } func get_line(json_data, sfx string) string { return ` var ctx` + sfx + ` = document.getElementById('chrt` + sfx + `').getContext('2d'); var jsonData` + sfx + ` = ` + json_data + `; var labels` + sfx + ` = Object.keys(jsonData` + sfx + `); var data` + sfx + ` = Object.values(jsonData` + sfx + `).map(Number); var myChart` + sfx + ` = new Chart(ctx` + sfx + `, { type: 'line', data: { labels: labels` + sfx + `, datasets: [{ label: 'Status', data: data` + sfx + `, fill: false, tension: 0.1 }] }, options: { scales: { y: { beginAtZero: true }, }, plugins: { title: { display: true, text: 'Line Chart Title', font: { size: 20, family: 'Source Sans Pro', } } } } }); ` }
4. The Go main code
package main import ( _ "embed" "net/http" "strings" "text/template" ) //go:embed mbd/chart.min.js var chartJS []byte var tpl *template.Template func init() { tpl = template.Must(template.New("").Funcs(template.FuncMap{ "chart": chart, }).ParseGlob("./public/tmpl/*/*.html")) http.Handle("/img/", http.StripPrefix("/img/", http.FileServer(http.Dir("./public/img")))) http.Handle("/css/", http.StripPrefix("/css/", http.FileServer(http.Dir("./public/css")))) http.Handle("/icn/", http.StripPrefix("/icn/", http.FileServer(http.Dir("./public/icn")))) http.Handle("/js/", http.StripPrefix("/js/", http.FileServer(http.Dir("./public/js")))) http.Handle("/misc/", http.StripPrefix("/misc/", http.FileServer(http.Dir("./public/misc")))) http.HandleFunc("/mbd/chart.min.js", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/javascript") w.Write(chartJS) }) } func main() { http.HandleFunc("/", endpoint) http.ListenAndServe(":9098", nil) } func endpoint(w http.ResponseWriter, r *http.Request) { module, mode, _ := getpath(r.URL.Path) var page string switch module { case "robots.txt": http.ServeFile(w, r, "public/misc/robots.txt") case "sitemap.xml": http.ServeFile(w, r, "public/misc/sitemap.xml") case "favicon.ico", "favicon-32x32.png", "favicon-16x16.png": return case "": module = "home" } switch mode { case "edit", "new", "view", "find", "goto", "status", "timer": default: page = module + ".html" set_header(w) // data := json2map(module, mode, val) tpl.ExecuteTemplate(w, page, nil) } } // split url func getpath(path string) (module, mode, val string) { parts := strings.Split(path, "/") switch len(parts) { case 4: val = parts[3] fallthrough case 3: mode = parts[2] fallthrough case 2: module = parts[1] } return // Named return values are used, so just return here } func set_header(w http.ResponseWriter) { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Methods", "*") }