How to execute multiple variables in HTML Go Template? - go

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}}

Related

go template is not receiving data passed when using 2 ParseFiles

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" . }}

Golang HTML insert new line after a comma

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

Golang how can I get full file path

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)
}

GO - get photos from flickr api

I'm new to Go. I'm trying to build a simple app that lets users get photos from flickr by keying in a tag in a form. I'm stuck at my current code (pasted below) as nothing appears on the page /showimage when I click on the submit button. What am I doing wrong in the code?
package main
import (
"encoding/json"
"fmt"
"html/template"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
)
func main() {
http.HandleFunc("/", handler)
http.HandleFunc("/showimage", showimage)
fmt.Println("listening...")
err := http.ListenAndServe(GetPort(), nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
func GetPort() string {
var port = os.Getenv("PORT")
if port == "" {
port = "4747"
fmt.Println("INFO: No PORT environment variable detected, defaulting to " + port)
}
return ":" + port
}
func handler (w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, rootForm)
}
const rootForm =
`<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Flickr photos</title>
</head>
<body>
<h1>Flickr photos</h1>
<p>Find photos by tags!</p>
<form action="/showimage" method="post" accept-charset="utf-8">
<input type="text" name="str" value="Type Tags..." id="str">
<input type="submit" value=".. and see the images!">
</form>
</body>
</html>`
var upperTemplate = template.Must(template.New("showimage").Parse(upperTemplateHTML))
func showimage(w http.ResponseWriter, r *http.Request) {
tag := r.FormValue("str")
safeTag := url.QueryEscape(tag)
apiKey := "MYAPIKEY"
fullUrl := fmt.Sprintf("https://api.flickr.com/services/rest/?method=flickr.people.getPhotos&api_key=%s&tags=%s", apiKey, safeTag)
client := &http.Client{}
req, err := http.NewRequest("GET", fullUrl, nil)
if err != nil {
log.Fatal("NewRequest: ", err)
return
}
resp, requestErr := client.Do(req)
if requestErr != nil {
log.Fatal("Do: ", requestErr)
return
}
defer resp.Body.Close()
body, dataReadErr := ioutil.ReadAll(resp.Body)
if dataReadErr != nil {
log.Fatal("ReadAll: ", dataReadErr)
return
}
res := make(map[string]map[string]map[string]interface{}, 0)
json.Unmarshal(body, &res)
owner, _ := res["photos"]["photo"]["owner"]
id, _ := res["photos"]["photo"]["id"]
queryUrl := fmt.Sprintf("http://flickr.com/photos/%s/%s", owner, id)
tempErr := upperTemplate.Execute(w, queryUrl)
if tempErr != nil {
http.Error(w, tempErr.Error(), http.StatusInternalServerError)
}
}
const upperTemplateHTML =
`<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Display images</title>
</head>
<body>
<h3>Images</h3>
<img src="{{html .}}" alt="Image" />
</body>
</html>`
Two things:
1) http.HandleFunc("showimage", showimage) should be http.HandleFunc("/showimage", showimage)
2) MYAPIKEY is probably not a valid api key.

HTTP-POST file multipart

I'm trying to send a multipart form using Go packages mime/multipart and http, and I need some help to solve it.
The HTML would be:
<html>
<head><title>Multipart Test</title></head>
<body>
<form action="/multipart" enctype="multipart/form-data" method="POST">
<label for="file"> Please select a File </label>
<input id="file" type="file" name="file"/>
<br>
<label for="input1"> Please write some text </label>
<input id="input1" type="text" name="input1"/>
<br>
<label for="input2"> Please write some more text </label>
<input id="input2" type="text" name="input2"/>
<br>
<input type="submit" name="Submit" value="Submit"/>
</body>
And my Go approach is like this:
var buffer bytes.Buffer
w := multipart.NewWriter(&buffer)
// Write fields and files
w.CreateFormField("input1")
w.WriteField("input1","value1")
w.CreateFormFile("file","filename.dat")
// I need a Reader to here to read the file, but how ?
// then send the request
resp,err := http.Post(url,w.FormDataContentType(),&buffer)
The answer can be found following this sample code
// Upload file to google code
func Upload(tarball string) (err os.Error) {
// Create buffer
buf := new(bytes.Buffer) // caveat IMO dont use this for large files, \
// create a tmpfile and assemble your multipart from there (not tested)
w := multipart.NewWriter(buf)
// Create a form field writer for field label
label, err := w.CreateFormField("label")
if err != nil {
return err
}
// Write label field
label.Write([]byte("label here"))
// Create a form field writer for field summary
summary, err := w.CreateFormField("summary")
if err != nil {
return err
}
// Write summary field
summary.Write([]byte("summary here"))
// Create file field
fw, err := w.CreateFormFile("upload", tarball)
if err != nil {
return err
}
fd, err := os.Open(tarball)
if err != nil {
return err
}
defer fd.Close()
// Write file field from file to upload
_, err = io.Copy(fw, fd)
if err != nil {
return err
}
// Important if you do not close the multipart writer you will not have a
// terminating boundry
w.Close()
req, err := http.NewRequest("POST", repoUrl, buf)
if err != nil {
return err
}
req.Header.Set("Content-Type", w.FormDataContentType())
req.SetBasicAuth("email#email.com", "password")
res, err := client.Do(req)
if err != nil {
return err
}
io.Copy(os.Stderr, res.Body) // Replace this with Status.Code check
return err
}

Resources