import "os"
import "html/template"
...
t, _ := template.ParseFiles("login.html")
t.Execute(os.Stdout, data)
...
login.html:
{{ template "header.html" . }}
<form ....>...</form>
{{ template "footer.html" . }}
no output, no error.
If I remove those two lines of {{ template "..." . }}, I could see the part being outputed.
What's required to make {{ template "..." . }} work or am I misunderstanding html/template completely?
You need to define a name for the file that will contain the other templates and then execute that.
login.tmpl
{{define "login"}}
<!doctype html>
<html lang="en">
..
{{template "header" .}}
</body>
</html>
{{end}}
header.tmpl
{{define "header"}}
whatever
{{end}}
Then you parse both those files
t := template.Must(template.ParseFiles("login.tmpl", "header.tmpl"))
// and then execute the template with the defined name
t.ExecuteTemplate(os.Stdout, "login", data)
Related
I'm playing with Go in a simple HTTP server:
// var tpl = template.Must(template.New("").Funcs(template.FuncMap{"isRegistered": isRegistered}).ParseGlob("templates/*")) // functions will be added later
var tpl = template.Must(template.ParseGlob("templates/*"))
func contact(w http.ResponseWriter, r *http.Request) {
//// defined templates are: "home.html", "layout", "layout.html", "contact.html", "body"
log.Println("in handler: ", tpl.DefinedTemplates())
err := tpl.ExecuteTemplate(w, "contact.html", nil)
if err != nil {
fmt.Println(err) // no error displayed
}
// fmt.Fprintf((w), "write") - This works fine
}
func main() {
log.Println("Serving on 8888 port")
http.HandleFunc("/contact", contact)
http.ListenAndServe(":8888", nil)
}
{{define "layout"}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{.Title}}</title>
<meta name="description" content="{{.Description}}">
<link rel="canonical" href="{{.Canonical}}" />
</head>
<body>
{{template "body" .}}
</body>
</html>
{{end}}
{{define "body"}}
<h1>Contact us page</h1>
<p>
Your name is...
</p>
{{end}}
The localhost:8888/contact returns OK 200 and empty body.
I used this example: https://stackoverflow.com/a/36643663/2110953
But I need to add template functions in future also:
var tpl = template.Must(template.New("").Funcs(template.FuncMap{"isRegistered": isRegistered}).ParseGlob("templates/*"))
Your contact.html does not "render" anything. It just defines the body template, but does not include it (execute it).
To execute a template (within a template), you may use the {{template}} action. To define and execute a template, you may use the {{block}} action.
Template Actions:
{{template "name"}}
The template with the specified name is executed with nil data.
{{template "name" pipeline}}
The template with the specified name is executed with dot set
to the value of the pipeline.
{{block "name" pipeline}} T1 {{end}}
A block is shorthand for defining a template
{{define "name"}} T1 {{end}}
and then executing it in place
{{template "name" pipeline}}
The typical use is to define a set of root templates that are
then customized by redefining the block templates within.
If your goal is to have a "fixed" header and footer in all pages, then you have to restructure your templates. Have a header and footer template defined somewhere, and the pages should include them as first and last elements. See How to use a field of struct or variable value as template name?
Update: So I had to just create a header and footer templates:
{{template "header" .}}
<h1>Contact us page</h1>
<p>
Your name is...
</p>
{{template "footer" .}}
{{define "header"}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{.Title}}</title>
<meta name="description" content="{{.Description}}">
<link rel="canonical" href="{{.Canonical}}" />
</head>
<body>
{{end}}
{{define "footer"}}
</body>
</html>
{{end}}
and it worked fine
How do i pass the data to the right template?
I have the following templates and want to parse them
layout.html:
<!DOCTYPE html>
<html>
<head>
...
</head>
<body>
<header>
...
</header>
<main>
{{template "main"}}
</main>
</body>
</html>
list.html:
{{define "main"}}
{{range $index, $element := . }}
<div>
<a href=#>{{ $element.Data1 }}</a>
<p>{{ $element.Data2 }}</p>
<p>{{ $element.Data3 }}</p>
</div>
{{end}}
{{end}}
When i use this in the handler func only the "main" template is executed and i dont get the layout.
t, err := template.ParseFiles(layoutPath, templatePath)
t.ExecuteTemplate(w, "main", Data)
And with this i dont have the Data in the list template and so cant display the list.
t, err := template.ParseFiles(layoutPath, templatePath)
t.ExecuteTemplate(w, Data)
So how do i execute this properly?
From the docs:
{{template "name" pipeline}} The template with the specified name is
executed with dot set to the value of the pipeline.
This means that to pass data from the layout.html template to the list.html template you need to pass the data as the second argument of the template action.
E.g. {{template "main" .}}
I'm trying to get a better and more efficient way for a role based templates use case.
You have three different roles with different contents, to simplify the example the sidebar is the same for every role.
The code and the template approach is very repetitive, there must be a better way to get it.
I could using some "if" sentences to load different templates, but that is slow.
If I use the new {{block}} action in the templates, it only saves 1 line for every content. I know that something is wrong here, but I can't get it.
Thanks in advance.
package main
import (
"html/template"
"log"
"os"
)
const t = `
{{define "header"}}==Header=={{end}}
{{define "sidebar"}}==Side Bar=={{end}}
{{define "footer"}}==Footer=={{end}}
{{define "index-role1"}}Welcome Stuff for Role1{{end}}
{{define "index-role2"}}Welcome Stuff for Role2{{end}}
{{define "index-role3"}}Welcome Stuff for Role3{{end}}
{{define "content1-role1"}}Content 1 for Role1{{end}}
{{define "content1-role2"}}Content 1 for Role2{{end}}
{{define "content1-role3"}}Content 1 for Role3{{end}}
{{define "content2-role1"}}Content 2 for Role1{{end}}
{{define "content2-role2"}}Content 2 for Role2{{end}}
{{define "content2-role3"}}Content 2 for Role3{{end}}
{{define "display-role1-index"}} {{template "header" .}}{{template "sidebar" . }} {{template "index-role1" .}}{{template "footer" .}} {{end}}
{{define "display-role1-content1"}} {{template "header" .}}{{template "sidebar" . }} {{template "content1-role1" .}}{{template "footer" .}}{{end}}
{{define "display-role1-content2"}} {{template "header" .}}{{template "sidebar" . }} {{template "content2-role1" .}}{{template "footer" .}}{{end}}
{{define "display-role2-index"}} {{template "header" .}}{{template "sidebar" . }} {{template "index-role2" .}}{{template "footer" .}}{{end}}
{{define "display-role2-content1"}} {{template "header" .}}{{template "sidebar" . }} {{template "content1-role2" .}}{{template "footer" .}}{{end}}
{{define "display-role2-content2"}} {{template "header" .}}{{template "sidebar" . }} {{template "content2-role2" .}}{{template "footer" .}}{{end}}
{{define "display-role3-index"}} {{template "header" .}}{{template "sidebar" . }} {{template "index-role3" .}}{{template "footer" .}}{{end}}
{{define "display-role3-content1"}} {{template "header" .}}{{template "sidebar" . }} {{template "content1-role3" .}}{{template "footer" .}}{{end}}
{{define "display-role3-content2"}} {{template "header" .}}{{template "sidebar" . }} {{template "content2-role3" .}}{{template "footer" .}}{{end}}
`
var templates map[string]*template.Template
type User struct {
Login string
Role string
}
func init() {
if templates == nil {
templates = make(map[string]*template.Template)
}
//Templates for role1
templates["role1-index"] = template.Must(template.New("display-role1-index").Parse(t))
templates["role1-content1"] = template.Must(template.New("display-role1-content1").Parse(t))
templates["role1-content2"] = template.Must(template.New("display-role1-content2").Parse(t))
//Templates for role2
...
//Templates for role3
...
}
func main() {
loggedUser := User{Login: "Username1", Role: "role1"}
// ONLY FOR INDEX
switch loggedUser.Role {
case "role1":
err := templates["role1-index"].Execute(os.Stdout, nil)
if err != nil {
log.Println(err.Error())
}
case "role2":
err := templates["role2-index"].Execute(os.Stdout, nil)
if err != nil {
log.Println(err.Error())
}
case "role3":
err := templates["role3-index"].Execute(os.Stdout, nil)
if err != nil {
log.Println(err.Error())
}
}
...
//CODE FOR CONTENT1
...
//CODE FOR CONTENT2
...
}
EDIT:
I'm thinking if something like that could help...
const t = `
{{define "header"}}==Header=={{end}}
{{define "sidebar"}}==Side Bar=={{end}}
{{define "footer"}}==Footer=={{end}}
{{define "display-base"}}
{{template "header" .}}
{{template "sidebar" . }}
{{block "content" .}}Welcome {{block "role"}}Role 1{{end}}{{end}}
{{template "footer" .}}
{{end}}`
In my question I'm trying to simplify the things to explain what have in my head, the stuff into the template code like "Content 1 Role1" is only to indicate that there should be some html code only for Role1 role view.
I added more details in the original question code.
Simplifying code
Your code can be simplified considerably:
You only need to parse the templates once. You can use Template.ExecuteTemplate() to execute one from the collection designated by its name.
The template names are easily derivable from the role name (stored in user.Role), so you don't need any switches.
See this simplified solution which besides index also renders content1 and content2 yet is much shorter than yours:
const t = `...` // Your template source here
var templates = template.Must(template.New("all").Parse(t))
type User struct {
Login string
Role string
}
func main() {
u := User{Login: "Username1", Role: "role1"}
for _, name := range []string{"index", "content1", "content2"} {
templName := "display-" + u.Role + "-" + name
if err := templates.ExecuteTemplate(os.Stdout, templName, nil); err != nil {
log.Println(err.Error())
}
}
}
It outputs:
==Header====Side Bar== Welcome Role1==Footer== ==Header====Side Bar==
Content 1 Role1==Footer== ==Header====Side Bar== Content 2 Role1==Footer==
If you change user's role to role2:
==Header====Side Bar== Welcome Role2==Footer== ==Header====Side Bar==
Content 1 Role2==Footer== ==Header====Side Bar== Content 2 Role2==Footer==
You can create a helper renderFor() function:
func renderFor(u User) {
for _, name := range []string{"index", "content1", "content2"} {
templName := "display-" + u.Role + "-" + name
if err := templates.ExecuteTemplate(os.Stdout, templName, nil); err != nil {
log.Println(err.Error())
}
}
}
And calling it for multiple users:
renderFor(User{Login: "Username1", Role: "role1"})
fmt.Println()
renderFor(User{Login: "Username2", Role: "role2"})
Try it on the Go Playground.
Simplifying template
A way to simplify your templates is to not define separate templates for separate roles, but use template actions to render different content and/or pass different data to the execution to be included in the output (to result in different content). You have to pass the role and other required information when you execute your template, so it can be used to differentiate. For example all your templates can be substituted with these:
const t = `
{{define "header"}}==Header=={{end}}
{{define "sidebar"}}==Side Bar=={{end}}
{{define "footer"}}==Footer=={{end}}
{{define "index"}}Welcome {{.Role}}{{end}}
{{define "content1"}}Content 1 {{.Role}}{{end}}
{{define "content2"}}Content 2 {{.Role}}{{end}}
{{define "display-index"}} {{template "header" .}}{{template "sidebar" . }} {{template "index" .}}{{template "footer" .}} {{end}}
{{define "display-content1"}} {{template "header" .}}{{template "sidebar" . }} {{template "content1" .}}{{template "footer" .}}{{end}}
{{define "display-content2"}} {{template "header" .}}{{template "sidebar" . }} {{template "content2" .}}{{template "footer" .}}{{end}}
`
Note that the roleX is gone from the name of the templates, that was the point.
And executing the templates:
func renderFor(u User) {
for _, name := range []string{"index", "content1", "content2"} {
templName := "display-" + name
if err := templates.ExecuteTemplate(os.Stdout, templName, u); err != nil {
log.Println(err.Error())
}
}
}
Note that the user u is passed to ExecuteTemplate(). Try it on the Go Playground.
Your example might be too unrealistic and that's why we could drastically reduce it, but this is the way to go in more complex examples too.
Also note that by design philosophy, templates should not contain complex logic. If something is (or looks) too complex in templates, you should consider calculating the result in Go code and either pass the result as data to the execution, or register a callback function in the templates and have a template action call that function and insert the return value.
How can I set a variable in each template that I can use in other templates e.g.
{{ set title "Title" }}
in one template then in my layout
<title> {{ title }} </title>
Then when it's rendered
tmpl, _ := template.ParseFiles("layout.html", "home.html")
it will set the title according to whatever was set in home.html instead of having to make a struct for each view page when it isn't really necessary. I hope I made sense, thanks.
Just for clarification:
layout.html:
<!DOCTYPE html>
<html>
<head>
<title>{{ title }} </title>
</head>
<body>
</body>
</html>
home.html:
{{ set Title "Home" . }}
<h1> {{ Title }} Page </h1>
If you want to use the Value in another template you can pipeline it to the dot:
{{with $title := "SomeTitle"}}
{{$title}} <--prints the value on the page
{{template "body" .}}
{{end}}
body template:
{{define "body"}}
<h1>{{.}}</h1> <--prints "SomeTitle" again
{{end}}
As far as i know it is not possible to go upwards in the chain.
So layout.html gets rendered before home.html, so you cant pass a value back.
In your example it would be the best solution to use a struct and pass it from the layout.html to the home.html using the dot:
main.go
package main
import (
"html/template"
"net/http"
)
type WebData struct {
Title string
}
func homeHandler(w http.ResponseWriter, r *http.Request) {
tmpl, _ := template.ParseFiles("layout.html", "home.html")
wd := WebData{
Title: "Home",
}
tmpl.Execute(w, &wd)
}
func pageHandler(w http.ResponseWriter, r *http.Request) {
tmpl, _ := template.ParseFiles("layout.html", "page.html")
wd := WebData{
Title: "Page",
}
tmpl.Execute(w, &wd)
}
func main() {
http.HandleFunc("/home", homeHandler)
http.HandleFunc("/page", pageHandler)
http.ListenAndServe(":8080", nil)
}
layout.html
<!DOCTYPE html>
<html>
<head>
<title>{{.Title}} </title>
</head>
<body>
{{template "body" .}}
</body>
</html>
home.html
{{define "body"}}
<h1>home.html {{.Title}}</h1>
{{end}}
page.html
{{define "body"}}
<h1>page.html {{.Title}}</h1>
{{end}}
Also go has a nice documentation on how to use templates:
http://golang.org/pkg/text/template/
I'm using SHPAML (HAML for python) for Django, however, I'm facing problems converting SHPAML -> HTML because of whitespace issues when overriding some blocks, heres an example:
In skeleton.shpaml:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>{{ title }}</title>
{% comment %}
<link rel="shortcut icon" href="/static/images/favicon.ico" type="image/x-icon"/>
{% endcomment %}
{% if css_list %}
{% for css in css_list %}
<link type="text/css" rel="stylesheet" href="{{css_relative}}{{ css }}">
{% endfor %}
{% endif %}
{% if js_list %}
{% for js in js_list %}
<script type="text/javascript" src="{{js_relative}}{{ js }}">
</script>
{% endfor %}
{% endif %}
{% if no_cache %}
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Cache-Control" content="no-cache" />
{% endif %}
</head>
body
#nonFooter
#content
{% block header %} {% endblock %}
#maincontent
{% block content %} {% endblock %}
#footer
</html>
In index.shpaml:
{% extends "includes/skeleton.shpaml" %}
{% block content %}
asd
.test
.test2 | meh
{% endblock %}
In the end, my output is this:
!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Home | Zineified</title>
<link type="text/css" rel="stylesheet" href="/media/css/base.css">
<script type="text/javascript" src="/media/js/jquery-1.3.2.min.js">
</script>
<script type="text/javascript" src="/media/js/jquery.form.js">
</script>
<script type="text/javascript" src="/media/js/base.js">
</script>
</head>
body
#nonFooter
#content
#maincontent
asd
.test
.test2 | meh
#footer
</html>
As you can see, whitespace is not preserved in the blocks. The next line in index.shpaml goes straight down into the next line in skeleton.shpaml. How can I prevent this and retain whitespace through template extending?
It looks like the SHPAML preprocessor is not getting invoked BEFORE Django. What I typically do is write all my documents in SHPAML with a .shpaml extension, and then I convert them to Django with the .html extension, and then let Django do its magic. So you will want statements like "extends" and "include" to refer to the .html document that has already been preprocessed.
Your base shpaml doc will look something like this:
html
body
#main_page
{% block body %}
{% endblock %}
And then the document that extends it will look something like this:
{% extends 'base.html' %}
{% block body %}
p
This is a paragraph about {{ book }}...
{% endblock %}
And then you want to preprocess them BEFORE Django sees them. I usually preprocess them with a Python script right before doing "manage.py runserver."
from docs:
spaceless
Removes whitespace between HTML tags. This includes tab characters and newlines.
Example usage:
{% spaceless %}
<p>
Foo
</p>
{% endspaceless %}
This example would return this HTML:
<p>Foo</p>
Only space between tags is removed -- not space between tags and text.
You can also remove excess spaces/newlines manually, but that will reduce the readability of the template.
#maincontent
asd
You mean misalignment here? Well, align your index.shpaml accordingly:
{% extends "includes/skeleton.shpaml" %}
{% block content %}
asd
.test
.test2 | meh
{% endblock %}
I've wrote a simple script to recursively explore a directory and find all shpaml files and convert them to *.htm. Thought I'd share it:
#!/usr/bin/env python
#===============================================================================
# Recursively explore this entire directory,
# and convert all *.shpaml files to *.htm files.
#===============================================================================
import shpaml
import os, glob
count = 0
def main():
global count
cwd = os.path.dirname(os.path.abspath(__file__))
convert_and_iterate(cwd)
print("Done. Converted "+str(count)+" SHPAML files.")
def convert_and_iterate(path):
global count
for file in glob.glob(os.path.join(path,'*.shpaml')):
#getting generic name
file_basename = os.path.basename(file)
gen_name = os.path.splitext(file_basename)[0]
#opening shpaml file and converting to html
shpaml_file = open(file)
shpaml_content = shpaml_file.read()
html_content = shpaml.convert_text(shpaml_content)
#writing to *.htm
html_file = open(os.path.join(path,gen_name+".htm"),"w")
html_file.write(html_content)
#incrementing count
count += 1
#transverse into deeper directories
dir_list = os.listdir(path)
for possible_dir in dir_list:
if os.path.isdir(os.path.join(path,possible_dir)):
convert_and_iterate(os.path.join(path,possible_dir))
if __name__ == "__main__":
main()