Gin Template Basics
Introduction
When building web applications with Go's Gin framework, you'll often need to generate dynamic HTML pages that display data from your server. This is where Gin's template functionality comes in. Templates allow you to define HTML layouts with special placeholders that Gin will replace with actual data when rendering the page.
Gin leverages Go's built-in html/template
package but adds convenient helpers to make template rendering more straightforward. In this guide, we'll explore the basics of using templates in Gin applications, from setting up template rendering to displaying dynamic data.
Setting Up Template Rendering in Gin
Before you can use templates in your Gin application, you need to load and configure them. Let's start with the most basic setup:
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
router := gin.Default()
// Load HTML templates from the "templates" folder
router.LoadHTMLGlob("templates/*")
// Define a route that will render a template
router.GET("/", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", gin.H{
"title": "Gin Template Example",
})
})
router.Run(":8080")
}
In this example, LoadHTMLGlob
tells Gin to load all HTML templates from the "templates" directory. The c.HTML()
function renders the template, passing in:
- The HTTP status code
- The name of the template file
- A map or struct containing data to pass to the template
Creating Your First Template
Let's create a simple template file. Create a directory called templates
in your project root and add a file named index.html
:
<!DOCTYPE html>
<html>
<head>
<title>{{ .title }}</title>
</head>
<body>
<h1>Welcome to {{ .title }}</h1>
<p>This is a simple template example.</p>
</body>
</html>
When rendered, the {{ .title }}
placeholders will be replaced with the value of the title
field from the data we passed in the route handler.
Passing Data to Templates
You can pass various types of data to your templates:
router.GET("/user", func(c *gin.Context) {
c.HTML(http.StatusOK, "user.html", gin.H{
"title": "User Profile",
"name": "John Doe",
"age": 30,
"active": true,
"skills": []string{"Go", "JavaScript", "Python"},
})
})
Then in your templates/user.html
file:
<!DOCTYPE html>
<html>
<head>
<title>{{ .title }}</title>
</head>
<body>
<h1>{{ .title }}</h1>
<h2>User Details</h2>
<p>Name: {{ .name }}</p>
<p>Age: {{ .age }}</p>
<p>Account status:
{{ if .active }}
<span style="color: green">Active</span>
{{ else }}
<span style="color: red">Inactive</span>
{{ end }}
</p>
<h3>Skills:</h3>
<ul>
{{ range .skills }}
<li>{{ . }}</li>
{{ end }}
</ul>
</body>
</html>
This example demonstrates:
- Accessing simple string, integer, and boolean values
- Using conditionals with
if
statements - Looping through slices with
range
Template Syntax Basics
Here are some common template directives you'll use in Gin templates:
Variable Output
To output a variable's value, use double curly braces:
{{ .variableName }}
The dot (.
) refers to the current data passed to the template.
Conditionals
{{ if .condition }}
<!-- Content displayed when condition is true -->
{{ else }}
<!-- Content displayed when condition is false -->
{{ end }}
Loops with Range
{{ range .items }}
<!-- .items is the slice/array/map being iterated -->
<!-- Inside the range, . refers to the current item -->
{{ . }}
{{ else }}
<!-- Content displayed when items is empty -->
{{ end }}
Comments
You can add comments that won't be rendered in the final HTML:
{{/* This is a comment and won't be rendered */}}
Template Functions
Go templates include many built-in functions to manipulate data:
<p>{{ .text | lower }}</p> <!-- Convert to lowercase -->
<p>{{ len .items }}</p> <!-- Get length of an array/slice/string -->
<p>{{ if eq .name "John" }}Hello John!{{ end }}</p> <!-- Equality check -->
Some common built-in functions:
eq
: Equalne
: Not equallt
: Less thangt
: Greater thanand
: Logical ANDor
: Logical ORnot
: Logical NOTlen
: Length of array, slice, or stringindex
: Access map or array element
Template Composition
Gin supports template composition using the template
action:
<!-- base.html -->
<!DOCTYPE html>
<html>
<head>
<title>{{ .title }}</title>
<link rel="stylesheet" href="/static/css/main.css">
</head>
<body>
<header>
<h1>My Website</h1>
</header>
<main>
{{ template "content" . }}
</main>
<footer>
<p>© 2023 My Website</p>
</footer>
</body>
</html>
<!-- page.html -->
{{ define "content" }}
<div class="container">
<h2>{{ .pageTitle }}</h2>
<p>{{ .pageContent }}</p>
</div>
{{ end }}
To use nested templates, update your Gin setup:
// Load all templates
router.LoadHTMLGlob("templates/**/*")
router.GET("/page", func(c *gin.Context) {
c.HTML(http.StatusOK, "base.html", gin.H{
"title": "My Page",
"pageTitle": "Welcome to My Page",
"pageContent": "This is the page content.",
})
})
A Real-World Example: Product List
Let's create a more practical example that displays a list of products:
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
// Product represents a product in our store
type Product struct {
ID int
Name string
Price float64
InStock bool
Category string
}
func main() {
router := gin.Default()
router.LoadHTMLGlob("templates/*")
// Serve static files like CSS
router.Static("/static", "./static")
router.GET("/products", func(c *gin.Context) {
products := []Product{
{ID: 1, Name: "Laptop", Price: 999.99, InStock: true, Category: "Electronics"},
{ID: 2, Name: "Headphones", Price: 99.99, InStock: true, Category: "Electronics"},
{ID: 3, Name: "Keyboard", Price: 49.99, InStock: false, Category: "Electronics"},
{ID: 4, Name: "Coffee Mug", Price: 14.99, InStock: true, Category: "Kitchen"},
}
c.HTML(http.StatusOK, "products.html", gin.H{
"title": "Product Catalog",
"products": products,
})
})
router.Run(":8080")
}
And the template:
<!-- templates/products.html -->
<!DOCTYPE html>
<html>
<head>
<title>{{ .title }}</title>
<style>
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
tr:nth-child(even) { background-color: #f2f2f2; }
th { background-color: #4CAF50; color: white; }
.out-of-stock { color: red; }
.in-stock { color: green; }
</style>
</head>
<body>
<h1>{{ .title }}</h1>
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Price</th>
<th>Status</th>
<th>Category</th>
</tr>
</thead>
<tbody>
{{ range .products }}
<tr>
<td>{{ .ID }}</td>
<td>{{ .Name }}</td>
<td>${{ printf "%.2f" .Price }}</td>
<td>
{{ if .InStock }}
<span class="in-stock">In Stock</span>
{{ else }}
<span class="out-of-stock">Out of Stock</span>
{{ end }}
</td>
<td>{{ .Category }}</td>
</tr>
{{ else }}
<tr>
<td colspan="5">No products found</td>
</tr>
{{ end }}
</tbody>
</table>
</body>
</html>
This example shows:
- A more complex data structure (slice of structs)
- Formatting output with the
printf
function - Conditional styling based on data values
- Handling empty collections with the
else
clause inrange
Summary
In this guide, we've covered the basics of using templates with the Gin framework:
- Setting up template rendering with
LoadHTMLGlob
- Creating templates with placeholders for dynamic data
- Passing data to templates using
c.HTML()
- Using template directives like conditionals and loops
- Working with template functions and composition
- Building a real-world product list example
Templates are a powerful way to generate dynamic HTML in your Gin applications. They separate presentation from business logic, making your code more maintainable and easier to understand.
Additional Resources and Exercises
Resources
- Official Gin Documentation on HTML Rendering
- Go's html/template Package Documentation
- Go Templates Cheat Sheet
Exercises
-
Blog Post Template: Create a template for displaying blog posts with title, author, date, content, and tags.
-
Form Template: Build a template for a user registration form that displays validation errors next to each field.
-
Admin Dashboard: Design a dashboard template that shows different content based on the user's role (admin vs. regular user).
-
Template Inheritance: Create a base layout template and multiple page templates that extend it, each with different content sections.
-
Dynamic Navigation: Build a template with a navigation bar that highlights the current page and displays different menu items based on whether the user is logged in.
Remember that practice is key to mastering templates. The more you work with them, the more comfortable you'll become with their syntax and capabilities!
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)