Templates
package:
text/template
: when dealing with command line applications.html/template
: when dealing with webapps.
In the first chapter we had a cursory glance over the concept of templates. This chapter is dedicated entirely to templates. As we said previously, a web application responds to certain URLs and gives an html page to the browser which the browser then interprets and shows to the end user. This html page which is sent to the browser is what is called templates in the backend for we have a template which stores some variables, and in real time data is provided into the template which makes it a complete html page.
Let's take a practical example. Suppose we are building a micro blogging site. We would start with creating the front end in html. Our microblogging site will show Hi User
on the right corner.
In our static html we write this <p>Hi User</p>
But if we serve this page on our webserver it'll not change anything, it'll show Hi User
, the name of the user won't come magically, we have to put it into the page somehow, here we use a variable so in Go we'd approach this by using a variable named {{.Name}}
so our html now will be <p>Hi {{.Name}}</p>
. The .
is mandatory.
Now, this is the logic we apply to all our html pages, keeping such {{}}
variable expansion parameters and serving the value of the parameter while executing the template. If you are wondering how we'd do that, this is the syntax
These are the three parts of using templates, first you need to create types like we have created the Context
type, then we need to read the template, then we need to use the components of that type in our template file. So what remains now is passing an object of that type during template execution.
Every template requires the context object because that is what defines the data to be populated in the template.
Reading template:
template.Must
Must is a helper that panics if the error is non-nil where allFiles is populated as below:
For the sake of demonstration of how to parse multiple files we have used the ParseFiles
method to parse all the .html
files, you can use the ParseGlob
method which is available in the standard library.
The definition of ParseGlob is:
We have to specify the Pattern for the ParseGlob function, but we have passed the path and the pattern because just passing the pattern is useless, we need the path where the pattern will be applied to find the list of all files meeting the criteria.
Note
...
operator : allFiles is a string slice and allFiles... passes the function a parameter as a string.ParseFiles performance:
There is one point to note here about performance in parsing files, typically a template file won't change until there is some major change to the codebase so we should only parse the files once, rather than keep this code in each view handler and doing a
This block of code will unnecessarily read the html page each time while serving the response of a request, which means if ten people are using our blogging site then for each page they visit the code will read the html page, but there is no need to do things this way, we can read the html files once at the start and then use the
Lookup
method of the template class,
Sub templating
We learnt how to pass data to templates and display it in the html page, it so happens that a lot of code is used in all templates suppose the navigation bar or the header, then we need not write the same chunk everywhere, we can create a template to store that, and use sub templating {{template "_head.html" .}}
Here, we have identified a chunk of code which we want to replicate and put it in _head.html
. Then we put the above statement in each template file where we wish to have our header. This way templates become a lot smaller and don't contain replicated code everywhere. Do note that the .
before the first }
is intentional and it means that all the variables which were passed to the current template are passed to the sub template.
The sub templates which we create depends on our requirement, but the basic point behind it is that if we are going to repeat a block of HTML code then we should form it as a template.
The main point to note over here is that when we are going to use our templates or sub templates, all those html files need to be parsed. In templating we have a variable which stores all templates even if we aren't going to refer to that directly in our code using the Lookup
method on the template variable. The lookup method takes the name of the template. When the {{template _head.html .}}
is evaluated, it goes to our template variable and tries to find out the template parsed with the exact name, if it is not present then it doesn't complain by default, we should use Must
method if we want it to complain.
Example
file views/views.go
Looping through arrays
The {{ if .Tasks}}
block checks if the array is empty or not, if it is not then it'll go to the {{ .range .Tasks }}
which will loop through the array, then the {{.Title}}
will access the title and {{.Content}}
will access the Content
of that particular Task instance and we'll see all the tasks as a list.
Template variables
We have this scenario, we have a range of categories in the navigation drawer and if we visit the /category/study
, then our category name should be highlighted in the navigation drawer. We do this by storing the value of the .Navigation field - which tells if it is a Edit page/Trash page/Category page
Creating variables
Creating variables in templates is similar to creating variables in Go, the variable name needs to be prefixed by '$'.
For understanding why template variables are required, we need to go into the above block of code, when we are using the range
operator, we are parsing the array and the range block gets the elements of the block by default.
My Context type is
Hence when we do a {{range .Categories}}
we will be accessing each element as {{.}}
per loop. If we use any other valid variable here, like the .Navigation, then the block tries to find the .Navigation inside .Categories, which obviously isn't present.
Now we need to make the page aware of which category it is showing, should the user go to the /categories/
page.
The logic behind making that page category aware is that we create a CSS class to mark that particular category as active, but for that, we'd need to access the Category name and Navigation within the range .Categories
block, thus we create two variables, one to store the category name from the .Navigation
variable and use the if statement like
This will mark only that particular category as active and not all of them.
In templating logic the operator is first and then the two operands.
eq: equal, le: less than equal, ge: greater than equal
You can also use if else clause like below:
Here we had some complicated stuff, if our page is a search one, we had to show Results for : <query>
, pending, deleted,edit, completed for respective and /category/ if we are in the category. So we defined an empty URL and assigned the URL values according to the complicated if else structure.
Homework
Take the html pages from http://github.com/thewhitetulip/omninotesweb and modify them to suit our purposes
We would need to create one template each for the ones we mentioned in the above variable declaration, use templating as far
as possible and later check your results with http://github.com/thewhitetulip/Tasks, please do the exercise on your own first
and then only check the Tasks repository.
Implement a search interface. Take a query as input, search tasks for that query and return an html page with
the query highlighted in the resulting page.
Links
Last updated