I'm so nearly there - my last step is to insert a new line after a comma (where there is one). For example, I have a table:
Country
City
Dates
Mexico
Playa Del Carmen
05-12-2019,06-12-2019,07-12-2019,08-12-2019,09-12-2019
French Polynesia
Papeete
16-11-2019
Rather than having all the dates on one line I want to insert a new line after each comma or better still replace the comma with a new line as below:
Country
City
Dates
Mexico
Playa Del Carmen
05-12-2019
ignore
ignore
06-12-2019
ignore
ignore
07-12-2019
ignore
ignore
08-12-2019
ignore
ignore
09-12-2019
French Polynesia
Papeete
16-11-2019
It doesn't need to be separate rows it can just be a list within the one row. The code I am using in golang html is:
<table>
<tr>
<th><b>Country</b></th>
<th><b>City</b></th>
<th><b>Dates</b></th>
</tr>
<tr>
<td>
<ul>
{{ range .TourCountry }}
<li>{{ . }}</li>
{{ end }}
</ul>
</td>
<td>
<ul>
{{ range .TourCity }}
<li>{{ . }}</li>
{{ end }}
</ul>
</td>
<td>
<ul>
{{ range .TourDateString }}
<li>{{ . }}</li>
{{ end }}
</ul>
</td>
</table>
The code I am using in the main.go file is:
func main() {
// static folder
fs := http.FileServer(http.Dir("static"))
http.Handle("/static/", http.StripPrefix("/static/", fs))
http.HandleFunc("/", mainPage)
http.HandleFunc("/concert", concertPage)
http.HandleFunc("/tour", tourPage)
http.HandleFunc("/about", aboutPage)
http.HandleFunc("/locations", locationsPage)
port := ":8080"
fmt.Println("Server listen on port localhost:8080")
err := http.ListenAndServe(port, nil)
if err != nil {
log.Fatal("Listen and Serve", err)
}
}
func tourPage(w http.ResponseWriter, r *http.Request) {
listOfIds := r.URL.Query()["id"]
id, err := strconv.Atoi(listOfIds[0])
if err != nil {
handle500(w)
}
ArtistsFull, _, _, _, _, _, _, err := GetData()
if err != nil || len(ArtistsFull) == 0 {
if err == nil {
err = errors.New("empty ArtistsFull from GetData")
}
fmt.Printf("GetData() error: %+v", err)
handle500(w)
}
artist, err := GetFullDataByID(id, ArtistsFull)
if err != nil {
fmt.Printf("GetFullDataByID(%d) error: %+v", id, err)
handle400(w)
}
tmpl, err := template.ParseFiles("tour.html")
if err != nil {
fmt.Printf("tour.html, error: %+v", err)
handle500(w)
}
if err := tmpl.Execute(w, artist); err != nil {
fmt.Printf("Tour Page Execute(w, artist) (%v) error: %+v/n", artist, err)
handle500(w)
}
}
Any ideas on how I can do this?
You can split the comma separated string in Go and then pass the resulting slice to the template. Or you can add custom template functions (with Funcs) that split the string in the template and then you can use that custom function within the template. Or you can define a method on the . value's type that does the split and then call that method within the template. As you can see you have at least 3 options, choose which ever you like best.
type Data struct {
TourDateString []string
}
var template_file = `<td>
<ul>
{{ range .TourDateString }}
<li>{{ . }}</li>
{{ end }}
</ul>
</td>`
func main() {
slice := strings.Split("05-12-2019,06-12-2019,07-12-2019,08-12-2019,09-12-2019", ",")
data := Data{slice}
t := template.Must(template.New("t").Parse(template_file))
if err := t.Execute(os.Stdout, data); err != nil {
panic(err)
}
}
https://go.dev/play/p/jx8u7dr8-G_z
type Data struct {
TourDateString string
}
var template_file = `<td>
<ul>
{{ range (split .TourDateString ",") }}
<li>{{ . }}</li>
{{ end }}
</ul>
</td>`
func main() {
data := Data{"05-12-2019,06-12-2019,07-12-2019,08-12-2019,09-12-2019"}
t := template.Must(template.New("t").Funcs(template.FuncMap{
"split": strings.Split,
}).Parse(template_file))
if err := t.Execute(os.Stdout, data); err != nil {
panic(err)
}
}
https://go.dev/play/p/IYqzF82WGgO
type Data struct {
TourDateString string
}
func (d Data) SplitTourDateString() []string {
return strings.Split(d.TourDateString, ",")
}
var template_file = `<td>
<ul>
{{ range .SplitTourDateString }}
<li>{{ . }}</li>
{{ end }}
</ul>
</td>`
func main() {
data := Data{"05-12-2019,06-12-2019,07-12-2019,08-12-2019,09-12-2019"}
t := template.Must(template.New("t").Parse(template_file))
if err := t.Execute(os.Stdout, data); err != nil {
panic(err)
}
}
https://go.dev/play/p/Fnr42ZbvlfH
Related
Im trying to create a handler which then will compile 2 templates:
template.html which serves layout purposes and the actual page: config.html.
this code builds the page, but no data is passed:
func config(w http.ResponseWriter, r *http.Request) {
fpTemplate := filepath.Join("static", "template.html")
fpPage := filepath.Join("static", "config.html")
tmpl, err := template.ParseFiles(fpPage, fpTemplate)
if err != nil {
log.Println("webserver.config: " + err.Error())
}
vd := ViewData{&Settings}
err = tmpl.ExecuteTemplate(w, "template.html", vd)
if err != nil {
log.Println("webserver.config: " + err.Error())
}
}
and config.html like this:
{{define "title"}}
Config
{{end}}
{{define "body"}}
<p class="text-break">
{{ .}}
</p>
{{end}}
, when I run this code:
func config(w http.ResponseWriter, r *http.Request) {
fpTemplate := filepath.Join("static", "template.html")
fpPage := filepath.Join("static", "config.html")
//tmpl, err := template.ParseFiles(fpPage, fpTemplate)
tmpl, err := template.New("config.html").ParseFiles(fpPage, fpTemplate)
if err != nil {
log.Println("webserver.config: " + err.Error())
}
vd := ViewData{&Settings}
err = tmpl.ExecuteTemplate(w, tmpl.Name(), vd)
fmt.Println(err)
//err = tmpl.ExecuteTemplate(w, "template.html", vd)
if err != nil {
log.Println("webserver.config: " + err.Error())
}
}
I get error: template: no template "config.html" associated with template "config.html" and blank black page.
What im I missing here ?
Appreciated any help!
When you pass "vd" to ExecuteTemplate in first code, the data pass to main template and you must pass the data into "body" template when you called it on "template.html" like as:
{{ template "body" . }}
I've tried looking at the other questions, and they don't seem to help my situation.
I essentially need to have 2 if statements on my HTML page, but whenever I trigger the second tmpl.Execute() I essentially get the same page embedded within those if statements.
Here is a function that I am trying to get working:
func RemoveVehicle(w http.ResponseWriter, r *http.Request) {
conditionsMap := map[string]interface{}{}
username, _ := ExtractTokenUsername(r)
if username != "" {
conditionsMap["Username"] = username
}
t, err := template.ParseFiles("remove.html")
if err != nil {
http.Error(w, err.Error(), 500)
return
}
if r.Method != http.MethodPost {
t.Execute(w, conditionsMap) //Here I am trying to check to see if the user is logged in, and if not to return a false boolean that will trigger the else statement
return
}
db, err := sql.Open("mysql", "root:*******#tcp(127.0.0.1:3306)/my_db")
if err != nil {
fmt.Println("Connection Failed.")
panic(err.Error())
}
defer db.Close()
var car Vehicle
sqlStatement := `SELECT * FROM Vehicle`
rows, err := db.Query(sqlStatement)
if err != nil {
panic(err)
}
defer rows.Close()
var carSlice []Vehicle
for rows.Next() {
rows.Scan(&car.Id, &car.Date, &car.Brand, &car.Model, &car.Mileage, &car.Year, &car.rented, &car.Dayrate)
carSlice = append(carSlice, car)
}
if r.Method != http.MethodPost {
t.Execute(w, carSlice) // Then here I am trying to populate the form select with a few rows from a table
return
}
var id_ = r.FormValue("select")
fmt.Println(id_)
stmt, e := db.Prepare("DELETE FROM vehicle WHERE id=?")
ErrorCheck(e)
stmt.Exec(id_)
}
I have commented out the 2 parts that I am trying to get working simultaneously, but they work individually.
Here is the relevant HTML:
{{if .Username}}
<div><img src="images/kogdpilnmzhz9rhzceo2.png" alt="" width="65" height="65" class="addV_label"/></div>
<hr style="height:5px">
<form action="/remove" method="POST" source="custom" name="form">
<input type="hidden" name="xss-token" value=""/>
<div class="form-group">
<div>
<label class="addV_label">Select Vehicle </label>
<select name="select" class="form-control loginInput2" required="required">
{{range .}}
<option value="{{.Id}}">{{.Brand}} {{.Model}} ({{.Year}}), {{.Mileage}} miles | £{{.Dayrate}}/pd</option>
{{end}}
</select>
</div>
</div>
<div>
<button class="btn-block frgt_1 btn addV_btn" type="submit" value="remove">REMOVE</button>
</div>
</form>
{{else}}
<p> Access Denied, please login.Login</p>
{{end}}
I've tried to use a struct, but the carSlice is already a struct and conditionMap is a map.
What would be the best solution to tackling this situation?
First off, it is recommended to initialize the *template.Template types and the *sql.DB connection pool only once, during program start up. Both types are safe for concurrent use and can therefore be used by multiple handlers simultaneously.
var (
removeTemplate *template.Template
db *sql.DB
)
func init() {
var err error
removeTemplate, err = template.ParseFiles("remove.html")
if err != nil {
panic(err)
}
db, err = sql.Open("mysql", "root:*******#tcp(127.0.0.1:3306)/my_db")
if err != nil {
panic(err)
} else if err := db.Ping(); err != nil {
panic(err)
}
}
Now your handler can look something like the following:
func RemoveVehicle(w http.ResponseWriter, r *http.Request) {
// check if the user is logged in
username, _ := ExtractTokenUsername(r)
if len(username) == 0 {
// if not, render the template with no data, this
// will show the "please login" part of your template
if err := removeTemplate.Execute(w, nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
return
}
switch r.Method {
// if the method is GET, show the list of vehicles available
case http.MethodGet:
// select vehicles from db
rows, err := db.Query(`SELECT * FROM Vehicle`)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer rows.Close()
// scan rows
var vehicles []Vehicle
for rows.Next() {
var v Vehicle
err := rows.Scan(&v.Id, &v.Date, &v.Brand, &v.Model, &v.Mileage, &v.Year, &v.rented, &v.Dayrate)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
vehicles = append(vehicles, v)
}
if err := rows.Err(); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// now render the template with the data
// that you just retrieved from the database
data := map[string]interface{}{
"Username": username,
"Vehicles": vehicles,
}
if err := removeTemplate.Execute(w, data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
// if the method is POST, delete the vehicle
case http.MethodPost {
var id = r.FormValue("select")
if _, err := db.Exec("DELETE FROM vehicle WHERE id=?", id); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
}
And in your template, since a data passed in by the handler is a map with Username and Vehicles keys, you need to range over .Vehicles and not just the ..
{{if .Username}}
<div><img src="images/kogdpilnmzhz9rhzceo2.png" alt="" width="65" height="65" class="addV_label"/></div>
<hr style="height:5px">
<form action="/remove" method="POST" source="custom" name="form">
<input type="hidden" name="xss-token" value=""/>
<div class="form-group">
<div>
<label class="addV_label">Select Vehicle </label>
<select name="select" class="form-control loginInput2" required="required">
{{range .Vehicles}}
<option value="{{.Id}}">{{.Brand}} {{.Model}} ({{.Year}}), {{.Mileage}} miles | £{{.Dayrate}}/pd</option>
{{end}}
</select>
</div>
</div>
<div>
<button class="btn-block frgt_1 btn addV_btn" type="submit" value="remove">REMOVE</button>
</div>
</form>
{{else}}
<p> Access Denied, please login.Login</p>
{{end}}
I been searching around and can not find a way to get the full file path in Go . I have a regular html form and then I try to get all the information in the backend
<form method="post" enctype="multipart/form-data" action="/uploads">
<p><input type="file" name="my file" id="my file"></p>
<p>
<input type="submit" value="Submit">
</p>
func upload() {
f,h,err := r.FormFile("my file")
if err != nil {
log.Println(err)
http.Error(w,"Error Uploading",http.StatusInternalServerError)
return
}
defer f.Close()
println(h.Filename)
}
// This gets me the name of the file, I would like the full path of it
I have tried file path.dir() but that does not do anything
here is an example:
package main
import (
"fmt"
"path/filepath"
)
func main() {
abs,err := filepath.Abs("./hello.go")
if err == nil {
fmt.Println("Absolute:", abs)
}
}
As far as I know, you cannot get the filepath form the f value in your code. Because the file data is not stored in disk yet.
And you want to store the file to a path, you can do it this way.
f,h,err := r.FormFile("myfile")
if err != nil{
log.Println("err: ",err)
http.Error(w,"Error Uploading",http.StatusInternalServerError)
return
}
defer f.Close()
fmt.Println("filename: ",h.Filename)
bytes, err := ioutil.ReadAll(f)
if err != nil {
fmt.Println(err)
}
filepath := "./aa" //set your filename and filepath
err = ioutil.WriteFile("aa", bytes, 0777)
if err != nil {
fmt.Println(err)
}
I have the go templates (upload.tmpl.html) like this :
<html>
<body>
<div class="container">
<ul>
<li>current fileName : {{ .fileName}} </li>
</ul>
</body>
</html>
an handler uploadHandler.go with
func UploadHandler(c *gin.Context) {
file, header, err := c.Request.FormFile("file-upload")
if err != nil {
log.Fatal("Erreur dans la récupération de fichier")
}
//...
fileName := make(chan string)
go ReadCsvFile(bytes, fileName)
go func() {
for {
log.Info(<-fileName)
}
}()
c.HTML(http.StatusOK, "upload.tmpl.html", gin.H{
"fileName": <-fileName,
})
}
and the ReadCsvFile() method like that :
func ReadCsvFile(bytesCSV []byte, fileName chan string) {
r := bytes.NewReader(bytesCSV)
reader := csv.NewReader(r)
reader.Comma = ';'
records, err := reader.ReadAll()
if err != nil {
fmt.Println("Error:", err)
return
}
db, _ := databaseApp.OpenDatabase()
defer db.Close()
for _, record := range records {
fileName <- record[0]
product := &em.Product{
Name: record[0],
//...
}
db.Create(product)
}
fileName <- "done"
}
I try to display the current fileName of each line in the template, but it is possible to bind the channel into the template like this ? Because in this way the page does not load anymore.
Use Websockets. Here are some examples:
HTML/JavaScript:
<script>
var ws= new WebSocket("ws://yoursite.com");
ws.onmessage = function (event) {
console.log(event.data);
// $('#your-element').html(event.data);
}
</script>
Go Websockets:
func websocketSenderHandler(conn *websocket.Conn){
for {
msg := <- globalChannel
conn.WriteMessage(websocket.TextMessage, msg)
}
}
More Websockets in Go: golang.org/x/net/websocket
Other Example: https://github.com/golang-samples/websocket
here is my code :
m.Get("/", func(r render.Render) string {
t := template.New("some template")
toto := "titi"
templateh := "<html>Hello world! {{ toto }} <form name='input' action='../first' method='post' ><input type='texte' name='toto'><input type='submit' value='Submit'></form></html>"
t, _ = t.Parse(templateh)
var doc bytes.Buffer
err := t.Execute(&doc, toto)
if err != nil {
fmt.Println("There was an error:", err)
}
s := doc.String()
fmt.Println(s)
return s
})
and it returns me a runtime error: invalid memory address or nil pointer dereference
and i don't understand why ...
The call
t, _ = t.Parse(templateh)
returns the nil and error an error stating that the function "todo" is not defined. The template Execute method dereferences the nil pointer, resulting in the panic.
You should change two things:
Check and handle the error return from the parse call. This is a good place to use the template.Must helper function.
Fix the template by replacing {{ todo }} with {{.}}