Распаковка полей ‹select› с гориллой/схемой

Я использую gorilla/schema, чтобы распаковать r.PostForm в структуру.

Моя проблема в том, что я пытаюсь найти «разумный» способ получить выбранное значение элемента <select> таким образом, чтобы я мог легко использовать html/template для повторного выбора поля (т.е. при повторном заполнении формы из сеанс), отметив, что нет простого способа проверить равенство and, просто передав экземпляр структуры в 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" {

// 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'm using a schema-less datastore

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

... и что я хочу сделать, когда я передаю экземпляр листинга в шаблон:

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 как возможное решение, но я не уверен, как я могу использовать это, когда я хочу передать весь экземпляр списка в шаблон (т.е. вместо поля за полем). Я также хочу, если возможно, свести к минимуму ненужные поля в моей структуре. Я мог бы иметь логические поля для каждого значения (например, Fulltime bool, но тогда мне нужен код, чтобы изменить другие поля на «false», если пользователь вернется и редактирует что-то.

Есть ли способ добиться этого таким образом, чтобы он хорошо сочетался с ограничениями template/html?


person elithrar    schedule 21.10.2013    source источник
comment
Я рекомендую повторно заполнять формы с помощью JS, когда это возможно.   -  person thwd    schedule 21.10.2013
comment
@tomwilde Я не могу повторно заполнить представления, если только я не сохраню данные в файле cookie с JS. Это небезопасно, а мой проглоченный файл <textarea> превышает лимит файлов cookie в 4 КБ.   -  person elithrar    schedule 21.10.2013
comment
Вы можете вывести значения в виде JSON в блок ‹script› или загрузить их асинхронно через AJAX после загрузки страницы.   -  person thwd    schedule 21.10.2013
comment
Верно, но это не будет быстро, учитывая относительно большое (до 2000 слов) текстовое поле и, скорее всего, приведет к миганию пустых полей во время загрузки. Пользователи обычно нажимают кнопку «Предварительный просмотр формы», а затем возвращаются к форме, если им нужно внести коррективы. Поскольку я храню все это в сеансе, я могу просто delete() отправить его, иначе пусть об этом позаботится истечение срока действия сеанса. Также позволяет мне проверять форму между шагами. Решения JS для этого кажутся такими ненадежными (но если вы знаете хорошую библиотеку, я все равно посмотрю). Я основывал свой дизайн на реализации формы Django, которая довольно надежна.   -  person elithrar    schedule 21.10.2013


Ответы (2)


Вы можете написать представление для создания и представления элемента select:

{{define "select"}}
    <select name="{{.Name}}>
        {{range $a, $b := .Options}}
             <option value="{{print $a}}" {{if $a == .Selected}}>{{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": "Full Time",
        "part-time": "Part Time",
        "contract":  "Contract",
        "freelance": "Freelance",
    },
}

И установите поле Selected:

termSelect.Selected = "full-time"

И выведите фрагмент представления внутри вашего представления формы:

{{template "select" $termSelect}}

Где $termSelect будет вашим экземпляром SelectBlock.

person thwd    schedule 21.10.2013
comment
Благодаря cronos в #go-nuts я использую Go 1.2RC2, который включает оператор равенства (!): golang.org/doc/go1.2#text_template Учитывая, что ваш ответ является одним из лучших способов сделать это в Go 1.1.x, наряду с eq() FuncMap или с помощью reflect.DeepEquals Я приму это как ответ. Спасибо за вашу помощь в этом @tomwild. - person elithrar; 22.10.2013

Для тех, кто рассматривает это в будущем и использует Go v1.2 или более позднюю версию: text/template и html/template теперь предлагают оператор равенства (среди других новых операторов): http://golang.org/doc/go1.2#text_template

{{if eq .Term "full-time" }}selected{{end}}
...
{{if eq .Term "freelance" }}selected{{end}}
person elithrar    schedule 22.10.2013