web-dev-qa-db-fra.com

Variables à l'intérieur des modèles dans Golang

Quel est l'espace de noms des variables à l'intérieur de html/text modèles? Je pensais qu'une variable $x peut changer la valeur à l'intérieur d'un modèle, mais cet exemple me montre que je ne peux pas.

J'ai échoué lorsque j'ai essayé de regrouper les tournois en fonction de l'année - quelque chose comme ça ( http://play.golang.org/p/EX1Aut_ULD ):

package main

import (
    "fmt"
    "os"
    "text/template"
    "time"
)

func main() {
    tournaments := []struct {
        Place string
        Date  time.Time
    }{
        // for clarity - date is sorted, we don't need sort it again
        {"Town1", time.Date(2015, time.November, 10, 23, 0, 0, 0, time.Local)},
        {"Town2", time.Date(2015, time.October, 10, 23, 0, 0, 0, time.Local)},
        {"Town3", time.Date(2014, time.November, 10, 23, 0, 0, 0, time.Local)},
    }
    t, err := template.New("").Parse(`
{{$prev_year:=0}}
{{range .}}
    {{with .Date}}
        {{$year:=.Year}}
                    {{if ne $year $prev_year}}
                        Actions in year {{$year}}:
                {{$prev_year:=$year}}
            {{end}}
    {{end}}

        {{.Place}}, {{.Date}}
    {{end}}

    `)
    if err != nil {
        panic(err)
    }
    err = t.Execute(os.Stdout, tournaments)
    if err != nil {
        fmt.Println("executing template:", err)
    }
}
12
lofcek

Modifier: voir https://stackoverflow.com/a/52925780/1685538 pour une réponse plus à jour.


Réponse originale:

https://golang.org/pkg/text/template/#hdr-Variables :

La portée d'une variable s'étend à l'action "fin" de la structure de contrôle ("si", "avec" ou "plage") dans laquelle elle est déclarée, ou à la fin du modèle s'il n'y a pas une telle structure de contrôle.

Alors le $prev_year vous définissez avec {{$prev_year:=$year}} ne vit que jusqu'à .. la ligne suivante ({{end}}).

Il semble qu'il n'y ait aucun moyen de contourner cela.

La "bonne" façon de procéder consiste à retirer cette logique de votre modèle et à effectuer le regroupement dans votre code Go.

Voici un exemple de travail: https://play.golang.org/p/DZoSXo9WQR

package main

import (
    "fmt"
    "os"
    "text/template"
    "time"
)

type Tournament struct {
    Place string
    Date  time.Time
}

type TournamentGroup struct {
    Year        int
    Tournaments []Tournament
}

func groupTournamentsByYear(tournaments []Tournament) []TournamentGroup {
    if len(tournaments) == 0 {
        return nil
    }

    result := []TournamentGroup{
        {
            Year:        tournaments[0].Date.Year(),
            Tournaments: make([]Tournament, 0, 1),
        },
    }

    i := 0
    for _, tournament := range tournaments {
        year := tournament.Date.Year()
        if result[i].Year == year {
            // Add to existing group
            result[i].Tournaments = append(result[i].Tournaments, tournament)
        } else {
            // New group
            result = append(result, TournamentGroup{
                Year: year,
                Tournaments: []Tournament{
                    tournament,
                },
            })
            i++
        }
    }

    return result
}

func main() {
    tournaments := []Tournament{
        // for clarity - date is sorted, we don't need sort it again
        {"Town1", time.Date(2015, time.November, 10, 23, 0, 0, 0, time.Local)},
        {"Town2", time.Date(2015, time.October, 10, 23, 0, 0, 0, time.Local)},
        {"Town3", time.Date(2014, time.November, 10, 23, 0, 0, 0, time.Local)},
    }

    t, err := template.New("").Parse(`
{{$prev_year:=0}}
{{range .}}
    Actions in year {{.Year}}:
    {{range .Tournaments}}

            {{.Place}}, {{.Date}}
    {{end}}
    {{end}}

    `)
    if err != nil {
        panic(err)
    }
    err = t.Execute(os.Stdout, groupTournamentsByYear(tournaments))
    if err != nil {
        fmt.Println("executing template:", err)
    }
}
12
HectorJ

Dans go1.11 text/template et donc html/template est devenu capable de définir la valeur des variables existantes , ce qui signifie que le code original peut être fait fonctionner avec une très petite modification.

Changement

{{$prev_year:=$year}}

À

{{$prev_year = $year}}

Aire de jeux

4
Nick Craig-Wood

Comme mentionné par la réponse this , la portée de cette variable "réassignation" se termine par {{end}} bloquer. Par conséquent, en utilisant uniquement des variables standard il n'y a aucun moyen de contourner le problème et il doit être résolu dans le programme Go exécutant le modèle.

Dans certains cadres, cependant, ce n'est pas si simple (par exemple, protocol-gen-gotemplate).

La bibliothèque Sprig ajoute des fonctionnalités supplémentaires au langage de modèle standard. L'un d'eux sont des cartes mutables qui peuvent être utilisées de la manière suivante:

// init the dictionary (you can init it without initial key/values as well)
{{$myVar := dict "key" "value"}}

// getting the "key" from the dictionary (returns array) and then fetching the first element from that array
{{pluck "key" $myVar | first}}

// conditional update block
{{if eq "some" "some"}}
     // the $_ seems necessary because Go template functions need to return something
     {{$_ := set $myVar "key" "newValue"}}
{{end}}

// print out the updated value
{{pluck "key" $myVar | first}}

Ce petit exemple imprime:

value
newValue

Une approche pragmatique consisterait à utiliser un dictionnaire unique pour toutes les variables mutables et à les stocker sous leur nom de variable correspondant comme clé.

Référence:

2
Peter Ilfrich