Unpacking <select> fields with gorilla/schema

huangapple go评论104阅读模式
英文:

Unpacking <select> fields with gorilla/schema

问题

我正在使用gorilla/schemar.PostForm解析为一个结构体。

我的问题是,我正在尝试找到一种“合理”的方法来获取<select>元素的选定值,以便我可以轻松地使用html/template重新选择该字段(即在从会话重新填充表单时)。需要注意的是,没有一种简单的方法来测试相等性,并且只需将结构体的实例传递给RenderTemplate

为了说明我的问题:

type Listing struct {
    Id           string        `schema:"-"`
    Title        string        `schema:"title"`
    Company      string        `schema:"company"`
    Location     string        `schema:"location"`
    ...
    Term         string        `schema:"term"`
}
if r.Method == "POST" {

    // 省略了错误处理代码,但请相信,它是存在的!

    err = r.ParseForm()
    err = decoder.Decode(listing, r.PostForm)
    err = listing.Validate() // 检查字段长度,因为我使用的是无模式的数据存储
}
<label for="term">Term</label>
<select data-placeholder="Term...?" id="term" name="term" required>
    <option name="term" value="full-time">Full Time</option>
    <option name="term" value="part-time">Part Time</option>
    <option name="term" value="contract">Contract</option>
    <option name="term" value="freelance">Freelance</option>
</select>

当我将listing的实例传递给模板时,我想要能够做到以下操作:

renderTemplate(w, "create_listing.tmpl", M{
    "listing": listing,
})
<label for="term">Term</label>
<select data-placeholder="Term...?" id="term" name="term" required>
    <option name="term" value="full-time" {{ if .term == "full-time" }}selected{{end}}>Full Time</option>
    <option name="term" value="part-time"{{ if .term == "part-time" }}selected{{end}}>Part Time</option>
    <option name="term" value="contract" {{ if .term == "contract" }}selected{{end}}>Contract</option>
    <option name="term" value="freelance" {{ if .term == "freelance" }}selected{{end}}>Freelance</option>
</select>

显然,这样做是行不通的。我考虑过使用template.FuncMap作为可能的解决方案,但我不确定在我想要将整个listing实例传递给模板时如何使用它(即而不是逐个字段地传递)。如果可能的话,我还希望尽量减少结构体中不必要的字段。我可以为每个值(例如Fulltime bool)添加布尔字段,但如果用户返回并编辑其他字段,我还需要编写代码将其他字段更改为“false”。

有没有一种方法可以以与template/html的限制相协调的方式实现这一点?

英文:

I'm using gorilla/schema to unpack r.PostForm into a struct.

My issue is that I'm trying to figure out a "sensible" way to get the selected value of a &lt;select&gt; element in a way that allows me to easily use html/template to re-select the field (i.e. when re-populating the form from a session) noting that there isn't an easy way to test for equality and just by passing an instance of the struct to RenderTemplate.

To illustrate what I have:

type Listing struct {
	Id           string        `schema:&quot;-&quot;`
	Title        string        `schema:&quot;title&quot;`
	Company      string        `schema:&quot;company&quot;`
	Location     string        `schema:&quot;location&quot;`
        ...
	Term         string        `schema:&quot;term&quot;`
}

if r.Method == &quot;POST&quot; {

// error handling code removed for brevity, but trust me, it exists!

	err = r.ParseForm()
	err = decoder.Decode(listing, r.PostForm)
    err = listing.Validate() // checks field lengths as I&#39;m using a schema-less datastore

&lt;label for=&quot;term&quot;&gt;Term&lt;/label&gt;
      &lt;select data-placeholder=&quot;Term...?&quot; id=&quot;term&quot; name=&quot;term&quot; required&gt;
        &lt;option name=&quot;term&quot; value=&quot;full-time&quot;&gt;Full Time&lt;/option&gt;
        &lt;option name=&quot;term&quot; value=&quot;part-time&quot;&gt;Part Time&lt;/option&gt;
        &lt;option name=&quot;term&quot; value=&quot;contract&quot;&gt;Contract&lt;/option&gt;
        &lt;option name=&quot;term&quot; value=&quot;freelance&quot;&gt;Freelance&lt;/option&gt;
      &lt;/select&gt;

... and what I want to be able to do when I pass an instance of listing to the template:

renderTemplate(w, &quot;create_listing.tmpl&quot;, M{
		&quot;listing&quot;:              listing,
	})

 &lt;label for=&quot;term&quot;&gt;Term&lt;/label&gt;
          &lt;select data-placeholder=&quot;Term...?&quot; id=&quot;term&quot; name=&quot;term&quot; required&gt;
            &lt;option name=&quot;term&quot; value=&quot;full-time&quot; {{ if .term == &quot;full-time&quot; }}selected{{end}}&gt;Full Time&lt;/option&gt;
            &lt;option name=&quot;term&quot; value=&quot;part-time&quot;{{ if .term == &quot;part-time&quot; }}selected{{end}}&gt;Part Time&lt;/option&gt;
            &lt;option name=&quot;term&quot; value=&quot;contract&quot; {{ if .term == &quot;contract&quot; }}selected{{end}}&gt;Contract&lt;/option&gt;
            &lt;option name=&quot;term&quot; value=&quot;freelance&quot; {{ if .term == &quot;freelance&quot; }}selected{{end}}&gt;Freelance&lt;/option&gt;
          &lt;/select&gt;

Obviously this won't work. I've considered template.FuncMap as a possible solution but I'm not sure how I can use this when I want to pass the entire listing instance to the template (i.e. instead of field-by-field). I also want to, if possible, minimise unnecessary fields in my struct. I could have boolean fields for each value (i.e. Fulltime bool, but I then need code to change the other fields to "false" if the user goes back and edits things.

Is there a way to achieve this in way that meshes well with the limitations of template/html?

答案1

得分: 5

你可以编写一个视图来构建和表示一个select元素:

{{define "select"}}
    <select name="{{.Name}}">
        {{range $a, $b := .Options}}
             <option value="{{print $a}}" {{if $a == .Selected}}selected{{end}}>{{print $b}}</option>
        {{end}}
    </select>
{{end}}

对应的数据结构如下:

type SelectBlock struct {
    Name     string
    Selected string
    Options  map[string]string
}

然后实例化它:

termSelect := SelectBlock{
    Name:     "term",
    Selected: "",
    Options:  map[string]string{
        "full-time": "全职",
        "part-time": "兼职",
        "contract":  "合同工",
        "freelance": "自由职业",
    },
}

并设置Selected字段:

termSelect.Selected = "full-time"

在你的表单视图中输出视图片段:

{{template "select" $termSelect}}

其中$termSelect将是你的SelectBlock实例。

英文:

You could write a view to build and represent a select element:

{{define &quot;select&quot;}}
    &lt;select name=&quot;{{.Name}}&gt;
        {{range $a, $b := .Options}}
             &lt;option value=&quot;{{print $a}}&quot; {{if $a == .Selected}}&gt;{{print $b}}&lt;/option&gt;
        {{end}}
    &lt;/select&gt;
{{end}}

And the corresponding data structure:

type SelectBlock struct {
    Name     string
    Selected string
    Options  map[string]string
}

Then instantiate it:

termSelect := SelectBlock{
    Name:     &quot;term&quot;,
    Selected: &quot;&quot;,
    Options:  map[string]string{
        &quot;full-time&quot;: &quot;Full Time&quot;,
        &quot;part-time&quot;: &quot;Part Time&quot;,
        &quot;contract&quot;:  &quot;Contract&quot;,
        &quot;freelance&quot;: &quot;Freelance&quot;,
    },
}

And set the Selected field:

termSelect.Selected = &quot;full-time&quot;

And output the view fragment inside your form view:

{{template &quot;select&quot; $termSelect}}

Where $termSelect would be your instance of SelectBlock.

答案2

得分: 2

对于将来使用 Go v1.2 或更高版本的其他人来说,text/templatehtml/template 现在提供了一个相等运算符(以及其他新的运算符):http://golang.org/doc/go1.2#text_template

{{if eq .Term "full-time" }}selected{{end}}
...
{{if eq .Term "freelance" }}selected{{end}}

英文:

For others looking at this in the future, who are using Go v1.2 or later: text/template and html/template now offer an equality operator (amongst other new operators): http://golang.org/doc/go1.2#text_template

{{if eq .Term &quot;full-time&quot; }}selected{{end}}
...
{{if eq .Term &quot;freelance&quot; }}selected{{end}}

huangapple
  • 本文由 发表于 2013年10月21日 12:06:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/19486103.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定