11

構造体のコレクションが与えられた場合、「範囲」テンプレート イテレータを使用して、明示的にフィールドに名前を付けずに、構造体ごとに行を割り当て、フィールド値ごとに列を割り当てるテーブルを出力するにはどうすればよいですか?

container := []Node

type Node struct {
    Contact_id        int
    Employer_id       int
    First_name        string
    Middle_name       string
    Last_name         string
}

テンプレート コード:

{{range .container}}

<tr>
<td>{{.Prefix}}</td>
<td>{{.First_name}}</td>
<td>{{.Middle_name}}</td>
<td>{{.Last_name}}</td>

<td>{{.Contact_id}}</td>
<td>{{.Employer_id}}</td>

</tr>
{{end}}

を使用して値を反復しようとすると

{{range .container}}
 {{range .}}
 <td>{{.}}</td> 
{{end}}
{{end}}

値を反復処理できないと言われました。これを行うためのきれいな方法はありますか?

4

1 に答える 1

16

ではhtml/template、構造体のフィールドを反復処理できません。パッケージのドキュメントでは、次を読むことができます。

{{range パイプライン}} T1 {{end}}
パイプラインの値は、配列、スライス、マップ、またはチャネルでなければなりません。

つまり、パイプラインを構造体にすることはできません。次のいずれかが必要です。

  • 中間タイプを使用します。[][]interface{}、テンプレートに渡すコンテナー変数として
  • あなたが示したように、各セルを別々に入力してください
  • 構造体の値を反復可能な型に変換するテンプレート関数を作成する

構造体はコンパイル時に定義され、実行時にその構造を変更しないため、反復は必要なく、テンプレートでより明確になることはありません。私はそれに反対することをお勧めします。

編集

しかし、時には反射は良いことです。Brenden はまた、関数から返された値に対して range を実際に反復させることができることも指摘しました。リフレクションを使用する場合、これが最も簡単な方法です。

テンプレート関数を使用した完全な作業例:

package main

import (
    "html/template"
    "os"
    "reflect"
)

type Node struct {
    Contact_id  int
    Employer_id int
    First_name  string
    Middle_name string
    Last_name   string
}

var templateFuncs = template.FuncMap{"rangeStruct": RangeStructer}

// In the template, we use rangeStruct to turn our struct values
// into a slice we can iterate over
var htmlTemplate = `{{range .}}<tr>
{{range rangeStruct .}} <td>{{.}}</td>
{{end}}</tr>
{{end}}`

func main() {
    container := []Node{
        {1, 12, "Accipiter", "ANisus", "Nisus"},
        {2, 42, "Hello", "my", "World"},
    }

    // We create the template and register out template function
    t := template.New("t").Funcs(templateFuncs)
    t, err := t.Parse(htmlTemplate)
    if err != nil {
        panic(err)
    }

    err = t.Execute(os.Stdout, container)
    if err != nil {
        panic(err)
    }

}

// RangeStructer takes the first argument, which must be a struct, and
// returns the value of each field in a slice. It will return nil
// if there are no arguments or first argument is not a struct
func RangeStructer(args ...interface{}) []interface{} {
    if len(args) == 0 {
        return nil
    }

    v := reflect.ValueOf(args[0])
    if v.Kind() != reflect.Struct {
        return nil
    }

    out := make([]interface{}, v.NumField())
    for i := 0; i < v.NumField(); i++ {
        out[i] = v.Field(i).Interface()
    }

    return out
}

出力:

<tr>
    <td>1</td>
    <td>12</td>
    <td>Accipiter</td>
    <td>ANisus</td>
    <td>Nisus</td>
</tr>
<tr>
    <td>2</td>
    <td>42</td>
    <td>Hello</td>
    <td>my</td>
    <td>World</td>
</tr>

遊び場

于 2013-11-15T07:45:14.747 に答える