web-dev-qa-db-fra.com

Variable de modèle de Golang isset

J'ai créé une fonction pour vérifier si une variable est définie:

fm["isset"] = func(a interface{}) bool {
        if a == nil || a == "" || a == 0 {
            fmt.Println("is not set")
            return false
        }
        fmt.Println("is set")
        return false
    }

tmpl :=  template.Must(template.New("").Funcs(fm).ParseFiles("templates/header.html"))

err := tmpl.ExecuteTemplate(w, "header", templateData)

Dans le modèle, j'ai:

{{ if isset .Email }}
    email is set
{{ end }}

Cette fonction fonctionne si la variable est contenue par la templateData (qui est une structure personnalisée contenant une carte et une chaîne), mais me donne une erreur si la variable n'existe pas.

L'erreur est: 

executing "header" at <.Email>: can't evaluate field Email in type base.customData

Dans mon cas, "base.go" est le gestionnaire et "customData" est défini par: type customData struct{..}.

Je veux pouvoir réutiliser des modèles et afficher certaines sections uniquement si certaines variables sont envoyées à partir du gestionnaire. Avez-vous une idée de la manière dont je peux implémenter une vérification de variable isset du côté du modèle?

J'ai aussi essayé d'utiliser: {{ if .Email}} do stuff {{ end }} mais cela me donne aussi la même erreur.

Une idée?

6
Pascut

La manière recommandée

Premièrement, la méthode recommandée consiste à ne pas compter sur l'existence d'un champ struct. Bien sûr, il peut y avoir des parties facultatives du modèle, mais la condition pour décider de rendre une partie doit s’appuyer sur des champs qui existent dans tous les cas.

Le problème, et l’éviter en utilisant une carte

Si le type des données de modèle est une struct (ou un pointeur sur une structure) et qu'il n'existe aucun champ ou méthode portant le nom donné, le moteur de modèle renvoie une erreur pour cela.

Vous pourriez facilement vous débarrasser de cette erreur si vous utilisiez une carte, car les cartes peuvent être indexées avec des clés qu'elles ne contiennent pas et le résultat de cette expression index est la valeur zéro type de valeur (et non une erreur).

Pour démontrer, voir cet exemple:

s := `{{if .Email}}Email is: {{.Email}}{{else}}Email is NOT set.{{end}}`

t := template.Must(template.New("").Parse(s))
exec := func(name string, param interface{}) {
    fmt.Printf("\n%s:\n  ", name)
    if err := t.Execute(os.Stdout, param); err != nil {
        fmt.Println("Error:", err)
    }
}

exec("Filled map", map[string]interface{}{"Email": "as@as"})
exec("Empty map", map[string]interface{}{})

exec("Filled struct", struct {
    Email string
}{Email: "[email protected]"})
exec("Empty struct", struct{}{})

Sortie (essayez-le sur le Go Playground ):

Filled map:
  Email is: as@as
Empty map:
  Email is NOT set.
Filled struct:
  Email is: [email protected]
Empty struct:
  Error: template: :1:5: executing "" at <.Email>: can't evaluate field Email in type struct {}

S'en tenir à struct et fournir "isset"

Si vous devez ou souhaitez vous en tenir à une struct, cet "isset" peut être implémenté et fourni, je l'appellerai avail().

Cette implémentation utilise la réflexion, et afin de vérifier si le champ donné par son nom existe (est disponible), les données (wrapper) doivent également lui être transmises:

func avail(name string, data interface{}) bool {
    v := reflect.ValueOf(data)
    if v.Kind() == reflect.Ptr {
        v = v.Elem()
    }
    if v.Kind() != reflect.Struct {
        return false
    }
    return v.FieldByName(name).IsValid()
}

Exemple d'utilisation:

s := `{{if (avail "Email" .)}}Email is: {{.Email}}{{else}}Email is unavailable.{{end}}`

t := template.Must(template.New("").Funcs(template.FuncMap{
    "avail": avail,
}).Parse(s))
exec := func(name string, param interface{}) {
    fmt.Printf("\n%s:\n  ", name)
    if err := t.Execute(os.Stdout, param); err != nil {
        fmt.Println("Error:", err)
    }
}

exec("Filled struct", struct {
    Email string
}{Email: "[email protected]"})
exec("Empty struct", struct{}{})

Sortie (essayez-le sur le Go Playground ):

Filled struct:
  Email is: [email protected]
Empty struct:
  Email is unavailable.
6
icza

Si vous n'envoyez pas de données contenant une variable au modèle mais que vous utilisez {{ if .Variable }}, vous obtiendrez l'erreur suivante: 

exécution de "nom_modèle" dans <.Variable>: impossible d'évaluer le champ Variable in type handler.Data

Ma solution était d'envoyer la variable "Email" sous forme de booléen (false) afin de passer le contrôle de la fonction {{ if .Email }}. Mais c'est une solution à court terme que je n'aime pas.

Je suis inspiré par: https://stackoverflow.com/a/31527618/1564840 . Dans cet exemple, ils affichent un code HTML différent pour les utilisateurs authentifiés et non authentifiés. Vous verrez que dans les deux cas, ils envoient la variable "Connecté". Essayez de supprimer cette variable de la structure et exécutez la fonction. Vous recevrez l'erreur que j'ai mentionnée ci-dessus.

0
Pascut