Gin Template Functions
Template functions in Gin provide powerful ways to manipulate and format data directly in your HTML templates. These functions give you the ability to transform data, perform calculations, and control how information is displayed without having to pre-process everything in your route handlers.
Introduction to Template Functions
In Gin's templating system (which uses Go's standard html/template
package under the hood), template functions allow you to process data on-the-fly as it's being rendered into HTML. These functions can:
- Format dates and times
- Transform strings (uppercase, lowercase, etc.)
- Perform mathematical calculations
- Conditionally display content
- Modify collections of data (filtering, mapping, etc.)
By using template functions, you can keep your templates cleaner and more expressive while reducing the amount of data preparation in your controllers.
Built-in Template Functions
Gin inherits many useful built-in functions from Go's template system. Let's explore some of the most commonly used ones:
Basic Operations
{{ and .Value1 .Value2 }} // Logical AND
{{ or .Value1 .Value2 }} // Logical OR
{{ not .Value }} // Logical NOT
{{ eq .Value1 .Value2 }} // Equal comparison
{{ ne .Value1 .Value2 }} // Not equal comparison
{{ lt .Value1 .Value2 }} // Less than
{{ le .Value1 .Value2 }} // Less than or equal
{{ gt .Value1 .Value2 }} // Greater than
{{ ge .Value1 .Value2 }} // Greater than or equal
For example, in a template:
<!-- Only show the edit button if user is an admin -->
{{ if eq .User.Role "admin" }}
<button>Edit</button>
{{ end }}
String Manipulation
{{ print .Value1 .Value2 }} // Concatenate and print values
{{ printf "%.2f" .FloatValue }} // Format string (like fmt.Printf)
{{ len .String }} // Length of a string or slice
{{ index .Slice 0 }} // Access element at index
Example in use:
<p>Welcome, {{ print .User.FirstName " " .User.LastName }}</p>
<p>You have {{ len .Messages }} new messages.</p>
Custom Template Functions in Gin
While the built-in functions are useful, Gin allows you to define custom functions to meet your specific needs. Here's how to add your own template functions:
Step 1: Create a FuncMap
First, create a template.FuncMap
containing your custom functions:
import (
"html/template"
"strings"
"time"
"github.com/gin-gonic/gin"
)
func createMyTemplateEngine() *template.Template {
funcMap := template.FuncMap{
// Format a time according to layout
"formatDate": func(t time.Time) string {
return t.Format("Jan 02, 2006")
},
// Shortens text to specified length
"truncate": func(s string, length int) string {
if len(s) <= length {
return s
}
return s[:length] + "..."
},
// Checks if a user has a specific role
"hasRole": func(user interface{}, role string) bool {
// Extract role from user and compare
u, ok := user.(map[string]interface{})
if !ok {
return false
}
userRole, ok := u["role"].(string)
return ok && userRole == role
},
}
tmpl := template.New("").Funcs(funcMap)
// Load templates here
return tmpl
}
Step 2: Register with Gin
Next, integrate your template engine with Gin:
func setupRouter() *gin.Engine {
r := gin.Default()
// Create our custom template renderer
tmpl := createMyTemplateEngine()
// Load all templates from the templates directory
tmpl, err := tmpl.ParseGlob("templates/*")
if err != nil {
panic(err)
}
// Set the template engine for the router
r.SetHTMLTemplate(tmpl)
// Your routes here...
return r
}
Step 3: Use the Custom Functions in Templates
Now you can use these functions in your templates:
<div class="article">
<h2>{{ .Article.Title }}</h2>
<p class="date">Posted on {{ formatDate .Article.CreatedAt }}</p>
<p class="summary">{{ truncate .Article.Content 150 }}</p>
{{ if hasRole .CurrentUser "editor" }}
<div class="edit-controls">
<button>Edit</button>
<button>Delete</button>
</div>
{{ end }}
</div>
Practical Examples
Let's explore some more practical examples of using custom template functions:
Example 1: Dynamic Navigation Highlighting
// Add this to your FuncMap
"isCurrentPath": func(currentPath, linkPath string) bool {
return currentPath == linkPath
},
In your template:
<nav>
<ul>
<li class="{{ if isCurrentPath .CurrentPath "/" }}active{{ end }}">
<a href="/">Home</a>
</li>
<li class="{{ if isCurrentPath .CurrentPath "/about" }}active{{ end }}">
<a href="/about">About</a>
</li>
<li class="{{ if isCurrentPath .CurrentPath "/contact" }}active{{ end }}">
<a href="/contact">Contact</a>
</li>
</ul>
</nav>
Example 2: Currency Formatting
// Add this to your FuncMap
"formatCurrency": func(amount float64) string {
return fmt.Sprintf("$%.2f", amount)
},
In your template:
<div class="product">
<h3>{{ .Product.Name }}</h3>
<p>Original Price: <span class="price">{{ formatCurrency .Product.Price }}</span></p>
{{ if gt .Product.DiscountPercentage 0 }}
<p>Sale Price: <span class="sale-price">
{{ formatCurrency (multiply .Product.Price (subtract 1 (divide .Product.DiscountPercentage 100))) }}
</span></p>
{{ end }}
</div>
Note: For the above example, you'd need to add multiply
, subtract
, and divide
functions to your FuncMap as well:
"multiply": func(a, b float64) float64 {
return a * b
},
"subtract": func(a, b float64) float64 {
return a - b
},
"divide": func(a, b float64) float64 {
if b == 0 {
return 0 // Protect against division by zero
}
return a / b
},
Example 3: Working with Collections
// Add to your FuncMap
"filter": func(items []interface{}, key, value string) []interface{} {
var filtered []interface{}
for _, item := range items {
m, ok := item.(map[string]interface{})
if !ok {
continue
}
if m[key] == value {
filtered = append(filtered, item)
}
}
return filtered
},
In your template:
<h2>Active Tasks</h2>
<ul>
{{ range filter .Tasks "status" "active" }}
<li>{{ .Title }} - {{ .DueDate }}</li>
{{ end }}
</ul>
Best Practices for Template Functions
- Keep functions simple and focused: Each function should do one thing well.
- Document your custom functions: Make sure everyone on your team understands what each function does.
- Handle edge cases: Ensure your functions handle nil values and other edge cases gracefully.
- Avoid complex logic in templates: While template functions are powerful, complicated business logic should still live in your Go code.
- Consider performance: Functions are called repeatedly during template rendering, so make them efficient.
Common Pitfalls
- Type issues: Template functions receive interface types, so proper type assertions are important.
- Scope confusion: Variables defined in one template block aren't accessible in another.
- Too much logic in templates: Templates should primarily handle presentation, not complex business logic.
- Not handling nil values: Always check for nil values to avoid panics during rendering.
Summary
Gin template functions provide a powerful way to extend your templates with custom behavior. Whether you're formatting dates, transforming text, or conditionally showing UI elements, template functions help you create dynamic and expressive templates while keeping your Go code clean and focused.
By defining your own custom template functions, you can create a more robust templating system tailored to your application's specific needs. Just remember to keep your functions simple, focused, and well-documented.
Additional Resources
Exercises
- Create a template function that formats a duration (like "5 minutes ago", "2 hours ago").
- Build a set of template functions for working with arrays (map, filter, some, etc.).
- Create a function that generates pagination controls based on current page and total pages.
- Implement a markdown-to-HTML template function to render user-generated content.
- Create a function that formats file sizes in human-readable form (KB, MB, GB).
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)