Skip to content

Latest commit

 

History

History
190 lines (146 loc) · 5.71 KB

File metadata and controls

190 lines (146 loc) · 5.71 KB

URLFor Functionality

Generate type-safe URLs for your pages:

Setup for Templ Templates

First, create a wrapper function for use in templ files:

// urlFor wraps structpages.URLFor for templ templates
func urlFor(ctx context.Context, page any, args ...any) (templ.SafeURL, error) {
    url, err := structpages.URLFor(ctx, page, args...)
    return templ.URL(url), err
}

Basic Usage

// Simple page references without parameters
<a href={ urlFor(ctx, index{}) }>Home</a>
<a href={ urlFor(ctx, product{}) }>Products</a>
<a href={ urlFor(ctx, team{}) }>Our Team</a>

With Path Parameters

// Route definition
type pages struct {
    userProfile `route:"/users/{id} User Profile"`
    blogPost    `route:"/blog/{year}/{month}/{slug} Blog Post"`
}

// In Go code (e.g., in handlers or middleware)
url, err := structpages.URLFor(ctx, userProfile{}, "123")
// Returns: /users/123
// Single parameter - positional
<a href={ urlFor(ctx, userProfile{}, "123") }>View User</a>

// Multiple parameters - as key-value pairs
<a href={ urlFor(ctx, blogPost{}, "year", "2024", "month", "06", "slug", "my-post") }>
    Read Post
</a>

// Using a map
<a href={ urlFor(ctx, blogPost{}, map[string]any{
    "year": "2024",
    "month": "06",
    "slug": "my-post",
}) }>Read Post</a>

With Query Parameters

Use the join helper to add query parameters:

// Helper function
func join(page any, pattern string) []any {
    return []any{page, pattern}
}
// Add query parameters with template placeholders
<a href={ urlFor(ctx, join(product{}, "?page={page}"), "page", "2") }>
    Page 2
</a>

// Multiple query parameters
<form hx-post={ urlFor(ctx, join(toggle{}, "?redirect={url}"), 
    "id", todoId, 
    "url", currentURL) }>
    <button>Toggle</button>
</form>

// Complex example with path and query parameters
<a href={ urlFor(ctx, join(jobDetail{}, "?tab={tab}"), 
    "id", jobId, 
    "tab", "overview") }>
    Job Overview
</a>

Automatic URL Parameter Extraction

When calling URLFor within a handler, URL parameters from the current request are automatically available and will be used to fill matching parameters in the generated URL. This is particularly useful when generating URLs for related pages that share the same parameters.

// Route definitions
type pages struct {
    viewProduct `route:"GET /product/{id} View Product"`
    editProduct `route:"GET /product/{id}/edit Edit Product"`
}

// In the view handler
func (v viewProduct) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    // Generate edit URL - the {id} parameter is automatically extracted from current request
    editURL, _ := structpages.URLFor(r.Context(), editProduct{})
    // If current URL is /product/123, editURL will be "/product/123/edit"
    
    // You can still override extracted parameters if needed
    differentURL, _ := structpages.URLFor(r.Context(), editProduct{}, "456")
    // differentURL will be "/product/456/edit"
}

This feature works with multiple parameters as well:

type pages struct {
    viewPost `route:"GET /blog/{year}/{month}/{slug} View Post"`
    editPost `route:"GET /blog/{year}/{month}/{slug}/edit Edit Post"`
}

// In a template
templ (v viewPost) Page() {
    // All parameters (year, month, slug) are automatically available
    <a href={ urlFor(ctx, editPost{}) }>Edit this post</a>
    
    // Override just one parameter while keeping others
    <a href={ urlFor(ctx, viewPost{}, map[string]any{"slug": "different-post"}) }>
        View different post in same month
    </a>
}

This automatic extraction eliminates the need to manually pass parameters that are already present in the current request context, making URL generation more convenient and less error-prone.

ID and IDTarget - Consistent HTML IDs

The ID and IDTarget functions generate consistent HTML IDs from component method references, helping maintain consistency between template IDs, HTMX targets, and component names.

The Problem

When building HTMX applications, you often need to keep three things in sync:

  1. The ID attribute in your component template
  2. The HTMX target selector in your requests
  3. The component method name
// If you change "UserList" to "UserTable", you need to manually update:
<div id="user-list">...</div>  // Manual ID
<button hx-target="#user-list">Refresh</button>  // Manual target reference
templ (p TeamManagementView) UserList(users []User) { ... }  // Component name

The Solution

Use ID and IDTarget with method expressions to generate IDs automatically:

// In your component template - use ID() for HTML id attributes
templ (p TeamManagementView) UserList(users []User) {
    <div id={ structpages.ID(ctx, p.UserList) }>
        <!-- content -->
    </div>
}

// In HTMX attributes - use IDTarget() for hx-target (includes "#" prefix)
templ (p TeamManagementView) Page(props Props) {
    <button hx-get="/api/users"
            hx-target={ structpages.IDTarget(ctx, p.UserList) }>
        Refresh
    </button>
    <div id={ structpages.ID(ctx, p.UserList) }>
        @p.UserList(props.Users)
    </div>
}

Now when you rename UserList to UserTable using your IDE's refactoring tools, all references including p.UserList will be automatically updated!

Naming Convention

Both ID and IDTarget convert CamelCase/PascalCase to kebab-case:

  • ID(ctx, p.UserList)"user-list"
  • IDTarget(ctx, p.UserList)"#user-list"
  • ID(ctx, p.GroupMembers)"group-members"
  • ID(ctx, p.HTMLParser)"html-parser"

The only difference is that IDTarget adds the "#" prefix for CSS selectors, while ID returns the raw ID string.

See the HTMX integration guide for more detailed examples and usage patterns.