This is a creation in Article, where the information may have evolved or changed.
The Go official library offers two template libraries: Text/template and Html/template. The two libraries are similar, except that the html/template
HTML format has been specially processed, which needs to be used when outputting HTML-formatted code html/template
.
Using templates can help us write some common code, or provide a clear file layout, or provide a code generator.
The official documentation provides a good way to use the template, which text/template
provides the basics of how to use the template, such as Action, Argument, Pipeline, Variable, Function, template nesting introduction, the html/template
Context is introduced. This article assumes that you already understand these basics. If you are not sure, or have not used the template, you can refer to the reference document at the end of the study.
Although the text/template
official documentation on template nesting is simple to introduce, but for the use of nested templates for the actual development, as well as considerations are not described in detail, so this article focuses on the use of nested templates.
Although this article is named as a best practice for nested templates, it's just one of the best practices, to be precise. If the reader has other practical scenarios, or better deal with the template nesting scenarios, welcome to discuss.
When we develop a Web application, it is unavoidable to use the template.
Generally a site contains a lot of pages, such as news pages, registration pages, the list of articles and so on. Different news may use the same news layout template, different article detail page may also use the same article layout page, while the News layout template and Article layout page may also have some common things, such as header, footer, navigation bar and so on. How to extract these common things into a unified template?
This article will step through these techniques and first describe the use of nested files.
Parse vs Parsefiles vs Parseblob
Let's start by looking at how the three files are different. In fact, there are five of files:
12345 |
func Parsefiles (Filenames ... string) (*template, error) func string) (*template, error) func string) (*template, error) func (t *template) Parsefiles (Filenames ... string) (*template, error) func string) (*template, error) |
Parse
Used to parse a string that represents the content of the template, and the nested template is associated with the template.
123 |
Tmpl, err: = template. New ("name"). Parse (...) //Error checking elidederr = Tmpl. Execute (out, data) |
And
1234 |
Import "Html/template"... t, err: = template. New ("foo"). Parse (' \{\{define ' t "\}\}hello, \{\{.\}\}!\{\{end\}\} '" T ""<script>alert (' You have Been Pwned ') </script> ") |
ParseFiles
Used to parse a named set of file templates, when the template is defined in different files, using this method can produce an executable template, the template will be executed without error.
ParseGlob
ParseFiles
similar to this, it uses filepath.Glob
pattern matching to traverse files and generate templates for these files.
These two functions can also be *Template
used as methods. When used as a function, it returns the name of the template as the name of the first file, the template with the first file as the base template.
At the same time, the subsequent files will also generate templates as the template of the association, you can find it through the Lookup
method, because each template is saved its associated version:
Let's illustrate the use of nested templates with an example.
There are two files under the current folder.
Header.html
Footer.html
1 |
"Footer"\}\}body is \{\{. Body\}\}\{\{end\}\} |
Test procedure:
12345678910111213141516171819202122 |
package mainimport ( "html/template" "net/http" ) func handler (w http. Responsewriter, R *http. Request) {T, _: = template. Parsefiles ( "header.html" , ) Err: = T.execute ( W, map [string ]string { Span class= "string" "Title" : "My Title" , "Body" : Span class= "string" > "Hi This is my Body" }) if err! = nil {panic (Err)}}func main () {http. Handlefunc (, Handler) http. Listenandserve (, nil )}
|
Browser access http://localhost:8080/
will result in rendering:
12 |
Title is My titlebody is |
You can see that the results are basically in line with expectations. Nested header.html
footer
templates, rendering when the header.html
main template display, nested rendering footer.html
.
But the above results show a little bit of a problem, that is, the footer
rendering time does not show the body results, this is because the data is passed to the main template, nested template if you want to use this information, you need to be in a nested place to pass data to it. We can modify header.html
:
12 |
Title is \{\{. Title\}\}\{\{template "Footer" <div class= "" ></div>\}\} |
This footer.html
allows you to render using the incoming data:
In the use ParseFiles
, ParseGlob
function , the default is the path of the last part of the file as the template name, such as the template name of the file, a/foo
foo
but if the parameters in different folders under the same name, the last template file with the same name will " Overwrite "The previous duplicate file template, the official library implementation cannot save the duplicate template file."
Execute vs Executetemplate
The example above shows the use of a simple nested template, but if we exchange the order of two files, as follows:
12345678 |
func Handler (w http. Responsewriter, R *http. Request) {T, _: = template. Parsefiles ("footer.html""header.html"map[string]string{"Title "my title" "Body""Hi This is My Body"})ifnil {panic (ERR)}} |
After running the browser access, you will find no error, but also did not render anything.
This is because ' template '. Parsefiles ' Returns the template to the first master template. footer.html
there is nothing to render in this case.
In this case, we will need to display a template that specifies the rendering to be rendered:
123456789 |
func handler (w http. Responsewriter, R *http. Request) {T, _: = template. Parsefiles ( "footer.html" , ) //err: = T.execute (W, map[string]string{"title": "My title", "Body": "Hi this is My Body"}) E RR: = T.executetemplate (W, , map [string ]string {: "My title" , : "Hi this is My Body" }) if err! = nil {panic (Err)}} |
Use ExecuteTemplate
a template that can choose to render t
the association as the main template for rendering. In the example above we chose header.html
to be the main template, so it renders normally.
Or, the following method can be rendered normally, and it is basically equivalent to the above code (only the small difference of escape).
123456789 |
func handler (w http. Responsewriter, R *http. Request) {T, _: = template. Parsefiles ( "footer.html" , ) T = t.lookup ( Span class= "string" > "header.html" ) Err: = T.execute (W, map [ string ] string {: "My Title" , "Body" : "Hi This is my Body" }) //err: = T.executetemplate (W, "header.html", map[string]string{"title": "My title", "Body": "Hi This is my Body "}" if err! = nil {panic (Err)}} |
So you can see the Execute
ExecuteTemplate
difference between the features and when you want to render with the specified associated template, use it ExecuteTemplate
.
Best practices
With the foundation above, we can solve the problem that the article starts with: How to plan a template for a website?
Suppose there are three files:,, hello.html
header.html
footer.html
which hello.html
is the unified layout of a column, header.html
is the header of all the layout needs to be nested HTML, such as site description, CSS, navigation bar, including the footer.html
introduction of global JavaScript files , copyright notices, and so on.
We created two folders:,, put in a layouts
widgets
folder, put it in a hello.html
layouts
header.html
footer.html
widgets
folder,
Let's take these three files as an example to see how nested templates are used in Web sites.
But before I do this, I need to introduce a problem in the code above. As you can see, each of the above requests will read the template file and parse it, which can seriously affect the performance of the program, the solution is to pre-read these files and generate template objects, so that the request is directly using the parsed template object.
12345678910111213141516171819202122232425 |
varTemplatesMap[string]*template. TemplatefuncInit () {ifTemplates = =Nil{templates = Make(Map[string]*template. Template)}//templatesdir: = "./templates/"TemplatesDir: ="./"layouts, err: = FilePath. Glob (TemplatesDir +"Layouts/*.html")ifErr! =Nil{log. Fatal (Err)}widgets, err: = FilePath. Glob (TemplatesDir +"Widgets/*.html")ifErr! =Nil{log. Fatal (ERR)} for_, Layout: =Rangelayouts {files: =Append(Widgets, layout) Templates[filepath. Base (layout)] = template. Must (template. Parsefiles (Files ...)}} |
With an map
object, we save the template object for each layout.
Then provide a way to render the template:
123456789 |
func string Interface {}) Error {Tmpl, OK: = Templates[name]if !ok {return FMT. Errorf ("The template%s does not exist.", name)}w.header (). Set ("Content-type" "text/html; Charset=utf-8 ")return Tmpl. Executetemplate (w, name, data)} |
I used this method when developing the Web GUI for rpcx: Rpcx-ui.
Of course, through some transformations can also achieve other template design, such as a base.html
template, which defines the header,footer, but also nested body
template, through the different body template to achieve different template layout, such as the definition of the news.html
body to display news, music.html
defines the body of music, which is also a good template layout.
Reference documents
- https://golang.org/pkg/text/template/
- https://golang.org/pkg/html/template/
- https://elithrar.github.io/article/approximating-html-template-inheritance/
- Https://groups.google.com/forum/#!topic/golang-nuts/EweRbwa_tks
- Http://stackoverflow.com/questions/12224436/multiple-files-using-template-parsefiles-in-golang
- https://gohugo.io/templates/go-templates/
- Https://astaxie.gitbooks.io/build-web-application-with-golang/content/en/07.4.html