¿Es posible tener plantillas anidadas en Go usando la biblioteca estándar?

87

¿Cómo obtengo plantillas anidadas como las que tiene Jinja en el tiempo de ejecución de Python? TBC, lo que quiero decir es cómo tengo un montón de plantillas heredadas de una plantilla base, simplemente archivando en bloques de las plantillas base, como hace Jinja / django-templates. ¿Es posible usar solo html/templateen la biblioteca estándar?

Si eso no es una posibilidad, ¿cuáles son mis alternativas? El bigote parece ser una opción, pero ¿me estaría perdiendo esas características sutiles agradables html/templatecomo el escape sensible al contexto, etc.? ¿Qué otras alternativas hay?

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

Gracias por leer.

Sri Kadimisetty
fuente

Respuestas:

132

Sí, es posible. A html.Templatees en realidad un conjunto de archivos de plantilla. Si ejecuta un bloque definido en este conjunto, tiene acceso a todos los demás bloques definidos en este conjunto.

Si crea un mapa de tales conjuntos de plantillas por su cuenta, tiene básicamente la misma flexibilidad que ofrece Jinja / Django. La única diferencia es que el paquete html / template no tiene acceso directo al sistema de archivos, por lo que debe analizar y componer las plantillas por su cuenta.

Considere el siguiente ejemplo con dos páginas diferentes ("index.html" y "other.html") que heredan 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}}

Y el siguiente mapa de conjuntos de plantillas:

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

Ahora puede renderizar su página "index.html" llamando

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

y puede representar su página "other.html" llamando

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

Con algunos trucos (por ejemplo, una convención de nomenclatura coherente de sus archivos de plantilla), incluso es posible generar el tmplmapa automáticamente.

tux21b
fuente
3
¿Es posible tener datos predeterminados, por ejemplo, para "cabeza"?
gregghz
18
Solo agregaré aquí que para renderizar las plantillas reales que tuve que llamar tmpl["index.html"].ExecuteTemplate(w, "base", data).
hermansc
base.html se analiza y almacena dos veces. También puede usar la función Clone () como en golang.org/pkg/text/template/#example_Template_share
Maarten O.
3
Tengo problemas al pasar datos a una plantilla anidada. Los datos de {{ .SomeData }}no se mostrarán en la plantilla interna. Las obras exteriores.
0xAffe
importa si template.ParseFiles("index.html", "base.html")es template.ParseFiles("base.html", "index.html")?
shackra
10

tenga en cuenta que cuando ejecuta su plantilla base, debe pasar valores a las plantillas secundarias, aquí simplemente paso ".", para que todo se transmita.

la plantilla uno muestra {{.}}

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

la plantilla dos muestra {{.domains}} que se pasa al padre.

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

Tenga en cuenta que si usamos {{template "content".}} En lugar de {{template "content".}}, Los dominios no serían accesibles desde la plantilla de contenido.

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
fuente
5
Pasar el modelo es un detalle en el que estaba atrapado. ;) Gracias
Patrick
1
yo también - me tomó un poco entender :)
robert king
1
¡Qué! ¡No puedo creer que haya tanto significado en el pequeño punto al final del marcador de posición {{template}}! ¿Por qué diablos no se menciona eso en ninguna parte de los tutoriales, o incluso en la documentación oficial de Go? Estoy atónito ... ¡pero también muy feliz de haber encontrado tu respuesta! Muchas gracias, ahora mis plantillas con varios niveles de anidación funcionan maravillosamente.
Gwyneth Llewelyn
¡Exactamente, lo mismo que estaba tratando de averiguar!
devforfu
5

Habiendo trabajado con otros paquetes de plantillas, hoy en día trabajo principalmente con el paquete estándar html / template, supongo que fui ingenuo al no apreciar la simplicidad que proporciona y otras ventajas. Utilizo un enfoque muy similar para la respuesta aceptada con los siguientes cambios

no necesita ajustar sus diseños con una baseplantilla adicional , se crea un bloque de plantilla para cada archivo analizado, por lo que en este caso es redundante, también me gusta usar la acción de bloqueo proporcionada en la nueva versión de go, que le permite tener contenido de bloqueo predeterminado en caso de que no proporcione uno en las plantillas secundarias

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

y sus plantillas de página pueden ser las mismas 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}}

ahora para ejecutar las plantillas necesitas llamarlo así

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

Utilice Pongo , que es un superconjunto de plantillas Go que admite las etiquetas {{extensions}} y {{block}} para la herencia de plantillas, al igual que Django.

Robar
fuente
4

He estado volviendo a esta respuesta durante días, finalmente mordí la bala y escribí una pequeña capa de abstracción / preprocesador para esto. Básicamente:

  • Agrega la palabra clave 'extiende' a las plantillas.
  • Permite anular las llamadas 'definir' (por lo tanto, los valores predeterminados para greggory son posibles)
  • Permite llamadas de 'plantilla' no definidas, solo dan una cadena vacía
  • Establece el valor predeterminado de. en llamadas de 'plantilla' a. del padre

https://github.com/daemonl/go_sweetpl

demonio
fuente