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) |
ParseUsed 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> ") |
ParseFilesUsed 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.
ParseGlobParseFilessimilar 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.htmlthere 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.htmldefines 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