Go Templates - Simple and Powerful
By Donald Feury (dak425)
Summary
## Key takeaways - **Go's built-in template package**: Go includes a powerful and extendable text/template package in its standard library, usable without external frameworks for generating HTML, text, or boilerplate code. [00:10], [00:39] - **HTML vs. Text Templates**: Go offers both `text/template` and `html/template` packages. The HTML version includes extra features for escaping characters to prevent security vulnerabilities like script injection. [00:41], [00:51] - **Understanding the dot '.' in templates**: The 'dot' in Go templates represents the current data context, similar to 'this' in object-oriented languages. Understanding its scope, especially within loops or functions, is key to mastering template syntax. [01:30], [10:26] - **Conditional logic and loops in templates**: Go templates support control structures like `if` for conditionals and `range` for iterating over collections, allowing for dynamic content generation based on data. [06:04], [08:38] - **Extending templates with FuncMaps**: You can inject custom functionality into Go templates using `FuncMaps`, allowing you to call external functions (like `strings.ToUpper`) directly within your templates. [12:51], [14:01] - **Generating code with templates**: Go templates can be used to automatically generate code, such as Go struct definitions, by processing structured data, saving significant manual effort, especially for APIs with many types. [16:37], [21:40]
Topics Covered
- Go's Template Engine: More Than Just Web Pages?
- Mastering the Elusive 'Dot' in Go Templates.
- Unlock Advanced Go Templating with Custom Functions.
- Clean Up Output: Go Template Whitespace Trimming.
- Automate Boilerplate Code with Go Templates.
Full Transcript
hey y'all doing doll back there again
and did you know did you know that go
has a template engine baked in this
language yeah a fancy little thing that
saved frameworks like label and PHP has
like the blade template engine and if
you've used Ruby you've probably used
rails and rails has like that embedded
Ruby syntax for templates yeah instead
of using a full-blown framework you can
basically just get that baked into the
language with this little text template
package now here's the thing to note
there are actually two template packages
in go standard library this one which is
one I'm going to be using for all these
examples is text slash template and
there's another one specifically for
HTML now they have almost the exact same
functionality but the HTML one has a few
extra bits of functionality put into it
specifically to properly escape special
characters injuries tml to avoid certain
attack vectors like JavaScript script
tags being injected into your web page
and stuff now if we if we look at this
this is pretty straightforward to use
here's an example that they have on the
documentation and here is the syntax you
use when you're actually like
referencing like a value or action that
you're going to be like injecting a
value into your string and it's like
this double curly braces syntax um you
also see this this dot this this is
probably the most confusing part about
gos template syntax if you can wrap your
head around with this what this freaking
dot means within the various context is
you'll be using templates you've
basically gotten around like 90% of the
the headache around understanding how a
good template so let me just switch over
here and start loops start showing you
some examples so you'll see I have a
string here that just represents a very
simple template it just says hello and
then I am injecting again dot
whatever-the-hell dot is but I will
explain to you what not is
so if we we actually want to put
something in this use this and usually
as a template and then put some data
into it right so what I want to put in
till this is put my name in there so I'm
going to say I'm gonna store this in a
variable name is Donald now I want to
actually get a template back and
interact with so I'm gonna say templates
error equals was a template dot new and
you can name it and the name doesn't
necessarily matter it's more if you're
gonna be using multiple templates I'm
just gonna call it hello and we'll call
parse now parse takes two arguments it
takes whatever the string representation
of your template is so in this case it's
going to be my template string and then
it also can take in what does it I'm
pretty hard uh I said yeah I'm getting
it myself yeah it's just that so back
the run this so and of course you got a
check for errors as usual like this I'm
just gonna say log this is a panic this
is panic it said could not parse now
there's actually have a convenient way
of messing of setting up these new
templates where you don't have to arrow
check every time if that's okay which is
instead of doing it like this you can
actually wrap this template do in this
convenience function that the template
package has called template dot must and
what this will do is if this throws an
error at all it'll panic the runtime
will panic so if if you don't
necessarily need the hair that can't
handle the error because it doesn't work
anyway this actually prevent you from
having to error check it like this
so we have our template and we want to
put our data into it now so we're say
error equals template dot execute so
we're going to execute processing this
template with some kind of data so the
first argument is anything that
implements that satisfies the i/o dot
writer interface and so just for the
make this easy I'm gonna use the I'm
just gonna have a go to standard out so
standard out and then you pass in
whatever the data is that you're going
to put into the template so in that case
it's just named Oh
why are you go I can't I can't type
today if error excuse me
we'll see verse I panic could not
execute template so if I go run main
this what it should do it should just
print out hello Donald right okay okay
say and just to verify that let's say if
I change this to see my last name URI
and I run this again very good lol fury
so there's some there's a couple of
other interesting things you can do with
templates and that is you can I use a
lot of the things that you would
normally use like in why you're doing
normal programming a template such as
conditionals you can use the range
operator to reiterate over collections
of data which is very useful and so
let's do that as an example let's um I'm
gonna have another template string
template string equals let's see let's
let's print hello if my name is Donald
about
actually we're gonna do let's just do it
if if the value I pass in is excuse me
is false
we're just gonna print out nothing or
rip out like go away or something and if
it's true we're going to actually print
out like pillow so let's go do like it's
if dots yeah we're just gonna do that
if dots damn we're going to say I'm just
gonna say hello yeah like that then I
think get the boots and at the end of it
like that and I'm going to parse it like
I did before
templates equals template dot must new
hello to parse templates frame that's
not that's the wrong assignment variable
there I haven't read do error equals
template execute OS STV out and let's
just pass in 0 so 0 will evaluate to
being falsely so this should not print
out anything as we're ado I say that's
just I'm just gonna throw the error away
cuz I don't really need that right now
and this should I should work I think
those go go run man maybe yeah so it
didn't for anything now if I go back
over to it but change this to one so
it's a truth eval you and I run it it
prayed that hello again so let's uh
let's do a range over some values so
let's have some names a name it's gonna
be a slice of strings we have you know
Donald rody just have slices slices
strings like that so we're gonna have
another that was drink and this time
we're to do range over dots and we're
gonna do
hello dots like that and at this one I'm
going to explain what the dot Bank means
because this point you're part like what
the hell is the dot see that should be
oh wait no I gotta go weird and any time
you're doing like the UM the actions
like if and range you have to actually
put an end to basically say that you're
done with the action and we're gonna do
templates equals templates most template
new range our templates range for the
airway rules template dot X execute this
STD out and names so what this should do
actually let me change this one thing
real quick so it's the least read think
but a new line there so that should be
give us a bunch of new lines okay so you
see that I've actually put the new line
up here too but you see hello Donald
Hill Bob hello birdie so let me explain
what the dot thing is you're this point
you're probably understand would tell
the dot is best way for me to explain
what the dot is is if you've come from a
language that has the concept that this
we're self it'll make a lot of sense for
instance when you are in a an instance
of a class object from like say Java or
something
this would refer to like the actual
object that's calling the function right
in the scope of these templates at the
very top level so like maybe right here
the the dot every time you see dot just
think of the word this and when you
think of this it's referring to whatever
was passed into this template let's
execute so in this case this dot is this
named variable which in this case is
just it's just a string if you go to
again this template this dot is just
equal to this number 1 now the rate this
template the dot is equal to this top
level data I passed it in which is just
this slice but what happens when you go
into something that does like an
iteration like this within the context
of this action so this this range each
iteration this dot is equal to whatever
the value is that we are currently
evaluating so the first time that we are
going through this range iteration this
would be equal to not on the second
iteration it would be equal to Bob and
on the last one they'd be equal to
Brewery that's probably the most
straightforward way for me to explain it
if we were interacting with a struct or
something that had properties on it so
like instead of this being strings if
these were Struck's the head properties
you could say like dot and then the name
of the property to get the value off of
that's probably the most straightforward
we've wait for me to explain them now
here's something super neat that you can
do with the templates that I don't think
a lot of people realize you can do this
is you can actually inject additional
functionality that isn't normally
present when you're processing the
templates and you do that with something
that it uses called a func map so if I
go back here and let me see if I can
find it it is down here somewhere
type func map so you'll notice that
there's a function on the template type
here called funks says funks adds the
elements of the argument map to the
templates function map there's there's
like us a base set of functions you can
call and the when you're processing a
template but there's not a lot of them
this is essentially as you do is to
inject additional functions that you can
use to process the text and it says that
it the functions have to be they have to
either return like one value or two and
then the second value always has to be
of type error and what happens is if the
function actually returns an error when
you call like execute execute will
return a so let's um let's add something
to this this template that doesn't have
by default let's give it the Billy to
uppercase all of the strings if so we're
gonna have lets we're gonna use the
exact same template string okay with the
exam but what the exception is the me
paces back down here we're not going to
just do dot we're going to we're gonna
pass in a function that we're gonna call
two upper like that okay and this may
not make a lot of sense right now but it
will very shortly
okay upper so we're gonna say template
equals template dot must template dot
new funks and now I'm going to call was
it funk funk yes and we have to pass
into it a map and let me actually make
that map real quick so I'm gonna call it
a bump map equals a map of strings and
others say there it particularly they
can be whatever so I'm just gonna do
that like this make sure that's it's not
good pointing everything so I'm gonna
say that to upper that's not how you
spell tonight
and I'm gonna say we are there already
is a function in the NGO standard
packages to do this I believe it is
strings dot to upper I believe that's
what it is this I believe that's what it
is yeah so now we're going to say Fox is
equal to this funk map I never recall
parse and we wrap s in our template
string like that and we're gonna throw
this away is equal to template dot
execute OS STD out and we're gonna pass
in our names again so what this should
output is almost the exact same thing as
before but all the names should be
uppercase keep in mind this the upper
casing stuff is not included in the base
set of functions you can normally use in
the template package when you're
processing templates have I run this now
you'll see that the second set they're
all capitalized you've injected
additional functionality that wasn't
there before this is super convenient I
I did this once for a project that I
sort of dead as an experiment in fact
I'm that's the last example I'm going to
show you is a um excuse me an adaptation
of what I did once to essentially
generate code for me so I'm going to go
to it how it does have a totally
different file and like I called it
struck stock ooh yeah so
this is we're gonna pretend that I have
obtained some data somewhere and this
data is of this this structure has name
a description and some fields and a
field has a name a type name and what
we're going to do is we're going to use
this data to generate go struct
definitions for us so you see I've
already defined some data here you just
have to pretend that I got this from
somewhere else in practicality this
example is based off of when I used a
web scraper to scrape a services API
Docs to get the information about there
like the straight in the shape of their
pea types they either expected in
arguments or in responses and use it to
just spit out all these struct
definitions that me how to do it by hand
so you'll see this first one says it has
a name called server and the description
says details about a server it has three
fields an ID as an int that URL to
string and active it's bullying okay we
have another one channel the description
has three fields and then we have a role
it's kind of sort of based on Mike skied
me like discourse way it's laid out more
or less and that we have our template
and you'll see my template you can
actually load in a file instead of just
like parsing an actual just like string
verbatim so let me let's open that up
shall we see split struck templates like
this and here is the template and you're
gonna see a couple of other things I'm
going to explain too so here's our dot
now keep in mind I am passing in a slice
so this top-level dot will be the slice
so I'm ranging over the slice and then
since the things I'm ranging or the
things that are in this slice are strux
the dot here is just whichever
particular struck I'm looking at and I
am referencing a property on the struck
and you'll see that I am assigning the
description property as the
meant really struct and then we have
type and then the name will be the
actual name of the struct struct and
then oh you can you can just have one
set of curly brackets and that it won't
it won't think it's like a template
syntax and then again I'm ranging over
the fields because the fields and the
data struct is itself a slice so I can
range over it and reference each one of
those you'll see there's this little
like - mark at the end here this this
actually has this X's significance if
you have in your open brackets like this
if you have a minus sign with a space in
this case proceeding in this case would
be preceding it this will trim all the
whitespace the to the of the next
section because you'll notice before
when these printed out down here this
was basically kind of verbatim the way
it was but you can use this little minus
sign syntax like trim whitespace either
before after your text that's being
generated and then you have the name
which is the name of the type and then
type name which is just whatever type I
said it was then you know and we're done
with this range then we're done with
this range so
surely this doesn't actually just spit
out code for us that would be crazy
right so if we go down here you'll see
that I am I'm creating a buffer for me
to write the process template into it
because again remember this can take
anything that implements I Oh doc writer
and bytes thought the the buffer type
from the bytes package does do that and
then interestingly enough you know that
little neat utility that go can use to
format your code but you could actually
reference it in the code itself as if
it's in the package call format and it
has a function called source and in
source you pass into it a slice of bytes
representing your source code and it
gives you back a slice of bytes
representing the formatted code and
you'll see down at the end I just I
typecast the slice of bytes into a
string so I could just print out so
let's actually run that and see what it
does shall we we're gonna do go run
Struck's
that's not I suppose trucks trucks don't
go and would you look at that you have
basically pre-generated go code nice
right now this example was pretty like
not really that important but the
example that I was telling you about
that this is based off of it was
actually mixers API rip mixer by the way
because I got interested in it whenever
mixer like joint ninja and I noticed
they had really well documented API
documentation and I managed to write a
web scraper that scraped specifically
the the all of the information about
their types they used under API and holy
thank God did that by the way
because there was about 157 data types
in their API Docs imagine going through
in writing the type definition for 157
strokes no thank you
I basically wrote this in a way that it
took all that information ran it through
a template not dissimilar from this one
right here but this
a little different because there were
some constants and stuff too and boom I
got 157 strokes and in minutes rather
than me saying here for hours doing it
granted I had to clean some of them up
because there were some inconsistencies
in the verbage they were using to
describe certain parts of their data
types but just just imagine how much
time I saved from doing that so keep
that mind if you're going to write a go
package and it's a go package for an API
and they have their types for their API
very well laid out in their Docs
while I write the things up by hand you
could just scrape it with a web scraper
and just spit out all the boilerplate
code so you can get to actually writing
the actual like the actual functions of
stuff so yeah there you go that's all I
have for you today just to make you
aware that go actually has a really nice
template package built into it and it
makes it very it's very powerful and
very convenient to use it also very
extendable because you can pass it in
that func map it allows you to inject
functionality to do it if you liked the
video be sure to drop me a like
subscribe or follow where you have pins
on whatever you're watching is that
chair if you think anybody else would be
able to make use of it if you'd like to
support the channel support these
tutorial videos I should have some links
wherever this is posted again you don't
have to support it all but I would
greatly appreciate it and with that
y'all come on back and I see you next
[Music]
Loading video analysis...