Files
tankstopp-app/docs/TEMPL_OPTIMIZATION.md
T
2025-07-07 01:44:12 +02:00

9.6 KiB

TankStopp Templ Optimization

Overview

This document describes the optimization of TankStopp's template system using a-h/templ - a compile-time template system for Go that generates type-safe HTML templates.

Migration Summary

The application has been migrated from traditional HTML templates to a-h/templ templates, providing:

  • Type Safety: Templates are compiled to Go code with full type checking
  • Performance: Templates are compiled at build time, eliminating runtime parsing
  • Component Reusability: Modular components that can be composed together
  • Developer Experience: IDE support, auto-completion, and compile-time error checking

Project Structure

The new template organization follows a clean architecture:

tankstopp/internal/views/
├── components/
│   ├── layout.templ      # Base layout, navbar, footer, cards, etc.
│   ├── forms.templ       # Form components, inputs, buttons
│   └── icons.templ       # Icon components with SVG definitions
└── pages/
    ├── auth.templ        # Authentication pages (login, register)
    ├── dashboard.templ   # Dashboard page with statistics
    ├── fuelstops.templ   # Add/edit fuel stop pages
    ├── vehicles.templ    # Vehicle management pages
    └── settings.templ    # Settings page

Component Architecture

Layout Components (components/layout.templ)

Base Layout

templ BaseLayout(title string, user *models.User, username string) {
    // Full HTML document structure with navbar, footer, and content area
}

Navigation Components

  • Navbar() - Main navigation bar
  • NavItem() - Individual navigation items
  • UserDropdown() - User account dropdown
  • Footer() - Application footer

UI Components

  • Card() - Reusable card component
  • Alert() - Alert messages (success, error, warning, info)
  • EmptyState() - Empty state placeholder
  • PageHeader() - Page header with title and subtitle
  • Badge() - Status badges
  • ProgressBar() - Progress indicators
  • Modal() - Modal dialogs
  • Tabs() - Tab navigation
  • Pagination() - Pagination controls

Form Components (components/forms.templ)

Input Components

  • Input() - Basic text inputs
  • NumberInput() - Number inputs with validation
  • DateInput() - Date picker inputs
  • TextArea() - Multi-line text areas
  • Select() - Dropdown selects
  • PasswordInput() - Password inputs with visibility toggle

Specialized Selects

  • CurrencySelect() - Currency dropdown
  • VehicleSelect() - Vehicle dropdown
  • FuelTypeSelect() - Fuel type dropdown

Form Layout

  • Form() - Form wrapper
  • FormGroup() - Input group with label and hints
  • FormRow() - Form row wrapper
  • FormCol() - Form column wrapper
  • FormButtons() - Form action buttons
  • InputGroup() - Input with prefix/suffix

Icon Components (components/icons.templ)

Comprehensive icon system with 40+ icons:

  • Icon(name, size) - Basic icon component
  • IconWithClass(name, size, class) - Icon with custom classes

Available icons include: fuel, plus, home, car, settings, location, edit, trash, save, user, lock, etc.

Page Templates

Authentication Pages (pages/auth.templ)

  • LoginPage() - User login form
  • RegisterPage() - User registration form
  • AuthLayout() - Shared layout for auth pages

Dashboard (pages/dashboard.templ)

  • DashboardPage() - Main dashboard with statistics and fuel stops table
  • FuelStopsTable() - Reusable fuel stops table component
  • DashboardScript() - JavaScript for dashboard functionality

Fuel Stops (pages/fuelstops.templ)

  • AddFuelStopPage() - Add new fuel stop form
  • EditFuelStopPage() - Edit existing fuel stop form
  • AddFuelStopScript() - JavaScript for form functionality including:
    • Auto-calculation of total costs
    • Current date/time defaults
    • Nearby gas station finder using Overpass API
    • Form validation

Vehicles (pages/vehicles.templ)

  • VehiclesPage() - Vehicle management dashboard
  • VehicleCard() - Individual vehicle card component
  • AddVehiclePage() - Add new vehicle form
  • EditVehiclePage() - Edit vehicle form
  • VehicleBrandSelect() - Vehicle brand dropdown
  • Helper functions for vehicle statistics

Settings (pages/settings.templ)

  • SettingsPage() - Comprehensive settings page with:
    • Profile settings
    • Application preferences
    • Security settings
    • Data management (import/export)
    • Account management
  • SettingsScript() - JavaScript for settings functionality

JavaScript Integration

The templ templates include embedded JavaScript using the script template type:

script DashboardScript() {
    function applyFilters() {
        // JavaScript functionality
    }
}

This approach provides:

  • Type-safe JavaScript embedding
  • Scoped functionality per page
  • Compile-time validation of JavaScript references

Usage Examples

Basic Page Structure

templ MyPage(user *models.User, data MyData) {
    @components.BaseLayout("My Page", user, user.Username) {
        @components.PageHeader("Subtitle", "My Page Title")
        
        <div class="page-body">
            <div class="container-xl">
                @components.Card("Card Title", "icon-name") {
                    // Card content
                }
            </div>
        </div>
    }
}

Form with Validation

templ MyForm(data FormData) {
    @components.Card("Form Title", "form-icon") {
        @components.Form("post", "/submit") {
            @components.FormGroup("Field Label", "Help text") {
                @components.Input("field_name", "text", "Placeholder", data.Value, true)
            }
            
            @components.FormButtons("/cancel", "Save", "save")
        }
    }
}

Data Table

templ DataTable(items []Item) {
    @components.TableResponsive() {
        <thead>
            <tr>
                <th>Column 1</th>
                <th>Column 2</th>
                <th>Actions</th>
            </tr>
        </thead>
        <tbody>
            for _, item := range items {
                <tr>
                    <td>{ item.Field1 }</td>
                    <td>{ item.Field2 }</td>
                    <td>
                        @components.ButtonGroup() {
                            @components.EditButton("/edit/" + item.ID)
                            @components.DeleteButton("/delete/" + item.ID, item.Name)
                        }
                    </td>
                </tr>
            }
        </tbody>
    }
}

Build Integration

To generate the Go code from templ files:

# Install templ CLI
go install github.com/a-h/templ/cmd/templ@latest

# Generate templates
templ generate

# Or use the shorthand
templ fmt .  # Format templates
templ generate .  # Generate Go code

Handler Integration

In your HTTP handlers, use the generated template functions:

func DashboardHandler(w http.ResponseWriter, r *http.Request) {
    // Get data...
    
    // Render template
    component := pages.DashboardPage(user, username, stops, vehicles, totalStops, totalCost, avgConsumption, lastFillUp)
    component.Render(r.Context(), w)
}

Benefits

Performance

  • Templates are compiled to Go code at build time
  • No runtime template parsing
  • Minimal memory allocation
  • Type-safe rendering

Developer Experience

  • Full IDE support with auto-completion
  • Compile-time error checking
  • Refactoring support
  • Hot reload during development

Maintainability

  • Component-based architecture
  • Clear separation of concerns
  • Reusable UI components
  • Consistent styling and behavior

Security

  • Automatic HTML escaping
  • XSS protection by default
  • Type-safe data binding
  • Compile-time validation

Migration from HTML Templates

Before (HTML Templates)

<!DOCTYPE html>
<html>
<head>
    <title>{{.Title}} - TankStopp</title>
    <!-- CSS links -->
</head>
<body>
    <!-- Navbar HTML -->
    <div class="page-body">
        {{range .Items}}
        <div class="card">
            <div class="card-body">
                <h4>{{.Name}}</h4>
                <p>{{.Description}}</p>
            </div>
        </div>
        {{end}}
    </div>
    <!-- Footer HTML -->
</body>
</html>

After (Templ Templates)

templ MyPage(title string, items []Item) {
    @components.BaseLayout(title, user, username) {
        <div class="page-body">
            <div class="container-xl">
                for _, item := range items {
                    @components.Card(item.Name, "icon") {
                        <p>{ item.Description }</p>
                    }
                }
            </div>
        </div>
    }
}

Best Practices

  1. Component Composition: Build complex UIs by composing smaller components
  2. Type Safety: Leverage Go's type system for template data
  3. Reusability: Create reusable components for common UI patterns
  4. Performance: Use templ's compile-time optimization
  5. Maintainability: Keep templates focused and well-organized
  6. Security: Rely on templ's automatic escaping and validation

Future Enhancements

  • Internationalization: Add multi-language support
  • Theming: Dynamic theme switching
  • Progressive Enhancement: Enhanced JavaScript functionality
  • Accessibility: ARIA labels and keyboard navigation
  • Performance Monitoring: Template rendering metrics

Conclusion

The migration to a-h/templ provides a modern, type-safe, and performant template system that improves developer experience while maintaining excellent runtime performance. The component-based architecture makes the application more maintainable and provides a solid foundation for future enhancements.