简体   繁体   中英

Accessing struct variable outside of {{range .}} scope in golang html template

<!DOCTYPE html>
<html>
<head>
    <title> Test </title>
</head>
<body>
    <div>
        <h2>Reply</h2>
        <form action="/post/{{$threadID}}" method="POST">
        <input type="text" name="subject" />
        <input type="text" name="name" value="Anonymous" />
        <input type="text" name="message" />
        <input type="submit" value="submit" />
        </form>
    </div>
    <div>
        {{range .}}
        {{$threadID := .ThreadID}}
        <h3>{{.Subject}}</h3>
        <h3>{{.Name}}</h3>
        <div>{{.DatePosted}}</div>
        <div><p>{{.Text}}</p></div>
        <br /><br />
        {{end}}
    </div>
</body>

I have this template, there is a form at the top of the page that requires the threadID from ANY one of the Posts sent (they're all the same, a slice of all posts with a certain threadID), this obviously doesn't work, my only other idea was something along the lines of

{{range .}}
    {{if $threadID == nil}}
        $threadID := .ThreadID
        //build the form same as above
    {{end}}
    <h3>{{.Subject}}</h3>
    <h3>{{.Name}}</h3>
    <div>{{.DatePosted}}</div>
    <div><p>{{.Text}}</p></div>
    <br /><br />
{{end}}

Here is the Post structure and methods if any of the above is unclear.

type Post struct {
threadID int
subject string
name string
text string
date_posted string
}

func (p *Post) ThreadID()   int    { return p.threadID    }
func (p *Post) Subject()    string { return p.subject     }
func (p *Post) Name()       string { return p.name        }
func (p *Post) Text()       string { return p.text        }
func (p *Post) DatePosted() string { return p.date_posted } 

And the origin of the slice of posts sent to the template

threadID := r.URL.Path[len("/reply/"):]
replies, err := i.db.Query("SELECT * FROM latest_threads where thread_id="+threadID);

If the Thread Id can only be retrieved from the Post type itself, consider turning your slice of posts into a separate type. Give it a ThreadID method, which simply returns the id for the first post it contains, or zero if none exist.

type PostList []*Post

func (p PostList) ThreadId() int {
    if len(p) == 0 {
        return 0
    }
    return p[0].ThreadId
}

Pass this list into the template. Now you can reference it from the template, anywhere outside of the {{range .}} clause.

<form action="/post/{{.ThreadID}}" method="POST">

Exploit warning

As a side note, Little Bobby Tables has an issue with the SQL query. It's possible you are just posting it as a quick example. If not, be advised that your code is a recipe for SQL injection exploits . If the thread ID is numeric, then ensure you parse it as such, before passing it into the SQL query. Eg: Sanitize your inputs.

You can pass it all as one struct like so:

layoutData := struct {
    ThreadID int
    Posts []Post
} {
    ThreadID: threadID,
    Posts: Posts,
}

Then something like this will work

<!DOCTYPE html>
<html>
<head>
    <title> Test </title>
</head>
<body>
    <div>
        <h2>Reply</h2>
        <form action="/post/{{ .ThreadID }}" method="POST">
        <input type="text" name="subject" />
        <input type="text" name="name" value="Anonymous" />
        <input type="text" name="message" />
        <input type="submit" value="submit" />
        </form>
    </div>
    <div>
        {{range $post := .Posts}}
        <h3>{{ $post.Subject}}</h3>
        <h3>{{$post.Name}}</h3>
        <div>{{$post.DatePosted}}</div>
        <div><p>{{$post.Text}}</p></div>
        <br /><br />
        {{end}}
    </div>
</body>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM