Est-il possible d'avoir des modèles imbriqués dans Go en utilisant la bibliothèque standard?

87

Comment obtenir des modèles imbriqués comme Jinja dans le runtime python. TBC ce que je veux dire, c'est comment puis-je faire hériter d'un tas de modèles d'un modèle de base, en déposant simplement des blocs des modèles de base, comme le fait Jinja / django-templates. Est-il possible d'utiliser uniquement html/templatedans la bibliothèque standard.

Si ce n'est pas une possibilité, quelles sont mes alternatives. La moustache semble être une option, mais est-ce que je passerais alors à côté de ces jolies fonctionnalités subtiles telles html/templateque l'échappement sensible au contexte, etc.? Quelles sont les autres alternatives?

(Environnement: Google App Engin, Go runtime v1, Dev - Mac OSx lion)

Merci d'avoir lu.

Sri Kadimisetty
la source

Réponses:

132

Oui c'est possible. A html.Templateest en fait un ensemble de fichiers modèles. Si vous exécutez un bloc défini dans cet ensemble, il a accès à tous les autres blocs définis dans cet ensemble.

Si vous créez vous-même une carte de ces ensembles de modèles, vous avez fondamentalement la même flexibilité qu'offre Jinja / Django. La seule différence est que le package html / template n'a pas d'accès direct au système de fichiers, vous devez donc analyser et composer les modèles vous-même.

Prenons l'exemple suivant avec deux pages différentes ("index.html" et "other.html") qui héritent toutes deux de "base.html":

// Content of base.html:
{{define "base"}}<html>
  <head>{{template "head" .}}</head>
  <body>{{template "body" .}}</body>
</html>{{end}}

// Content of index.html:
{{define "head"}}<title>index</title>{{end}}
{{define "body"}}index{{end}}

// Content of other.html:
{{define "head"}}<title>other</title>{{end}}
{{define "body"}}other{{end}}

Et la carte suivante des ensembles de modèles:

tmpl := make(map[string]*template.Template)
tmpl["index.html"] = template.Must(template.ParseFiles("index.html", "base.html"))
tmpl["other.html"] = template.Must(template.ParseFiles("other.html", "base.html"))

Vous pouvez maintenant afficher votre page "index.html" en appelant

tmpl["index.html"].Execute("base", data)

et vous pouvez afficher votre page "other.html" en appelant

tmpl["other.html"].Execute("base", data)

Avec quelques astuces (par exemple une convention de dénomination cohérente de vos fichiers modèles), il est même possible de générer la tmplcarte automatiquement.

tux21b
la source
3
est-il possible d'avoir des données par défaut, par exemple, pour "tête"?
gregghz
18
Je vais juste ajouter ici que pour rendre les modèles réels que j'ai dû appeler tmpl["index.html"].ExecuteTemplate(w, "base", data).
hermansc
base.html est analysé et stocké deux fois. Vous pouvez également utiliser la fonction Clone () comme dans golang.org/pkg/text/template/#example_Template_share
Maarten O.
3
Je rencontre des problèmes lors de la transmission de données à un modèle imbriqué. Les données de {{ .SomeData }}ne seront pas affichées dans le modèle interne. Les œuvres extérieures.
0xAffe
ça compte si template.ParseFiles("index.html", "base.html")c'est template.ParseFiles("base.html", "index.html")?
shackra
10

notez que lorsque vous exécutez votre modèle de base, vous devez transmettre des valeurs aux modèles enfants, ici je passe simplement ".", pour que tout soit transmis.

le modèle un affiche {{.}}

{{define "base"}}
<html>
        <div class="container">
            {{.}}
            {{template "content" .}}
        </div>
    </body>
</html>
{{end}}

le modèle deux affiche {{.domains}} qui est passé au parent.

{{define "content"}}
{{.domains}}
{{end}}

Notez que si nous utilisions {{template "content".}} Au lieu de {{template "content".}}, .Domains ne serait pas accessible à partir du modèle de contenu.

DomainsData := make(map[string]interface{})
    DomainsData["domains"] = domains.Domains
    if err := groupsTemplate.ExecuteTemplate(w, "base", DomainsData); err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
Robert King
la source
5
Transmettre le modèle est un détail sur lequel j'étais coincé. ;) Merci
Patrick
1
moi aussi - a pris un peu de temps pour comprendre :)
robert king
1
Quoi! Je n'arrive pas à croire qu'il y ait autant de sens dans le petit point à la fin de l'espace réservé {{template}}! Pourquoi diable n'est-ce pas mentionné nulle part dans les tutoriels, ni même dans la documentation officielle de Go ?? Je suis sidéré ... mais aussi très heureux d'avoir trouvé votre réponse! Merci beaucoup, maintenant mes modèles avec plusieurs niveaux d'imbrication fonctionnent à merveille!
Gwyneth Llewelyn
Exactement, la même chose que j'essayais de comprendre!
devforfu
5

ayant travaillé avec d'autres packages de modèles, maintenant je travaille principalement avec des packages html / template standard, je suppose que j'étais naïf de ne pas apprécier la simplicité qu'il offre et d'autres goodies. J'utilise une approche très similaire à la réponse acceptée avec les modifications suivantes

vous n'avez pas besoin d'encapsuler vos mises en page avec un basemodèle supplémentaire , un bloc de modèle est créé pour chaque fichier analysé donc dans ce cas, il est redondant, j'aime aussi utiliser l'action de blocage fournie dans la nouvelle version de go, qui vous permet d'avoir contenu de bloc par défaut au cas où vous n'en fournissez pas un dans les modèles enfants

// base.html
<head>{{block "head" .}} Default Title {{end}}</head>
<body>{{block "body" .}} default body {{end}}</body>

et vos modèles de page peuvent être les mêmes que

// Content of index.html:
{{define "head"}}<title>index</title>{{end}}
{{define "body"}}index{{end}}

// Content of other.html:
{{define "head"}}<title>other</title>{{end}}
{{define "body"}}other{{end}}

maintenant pour exécuter les modèles, vous devez l'appeler comme ça

tmpl["index.html"].ExecuteTemplate(os.Stdout, "base.html", data)
allyraza
la source
4

Utilisez Pongo , qui est un super-ensemble de modèles Go prenant en charge les balises {{extend}} et {{block}} pour l'héritage de modèles, tout comme Django.

Rob
la source
4

Je reviens sur cette réponse depuis des jours, j'ai finalement mordu la balle et j'ai écrit une petite couche d'abstraction / pré-processeur pour cela. Il s'agit essentiellement:

  • Ajoute le mot-clé "extend" aux modèles.
  • Permet de remplacer les appels 'define' (ainsi les valeurs par défaut pour greggory sont possibles)
  • Autorise les appels de 'template' non définis, ils donnent juste une chaîne vide
  • Définit la valeur par défaut de. dans les appels de «modèle» à. du parent

https://github.com/daemonl/go_sweetpl

démonl
la source