Skip to content

Proposal: Support for String Interpolation and Templates in ht-content #4

Description

@thoward

Overview

Currently, the ht-content attribute only supports a simple comma-delimited fallback list (e.g., ht-content="page.title,site.title"). While effective for simple value replacement, it lacks the ability to combine multiple variables or wrap variables in decorative text (like pipes, dashes, or labels) at build time.

This proposal introduces Braced Interpolation for ht-content. This allows developers to treat the attribute value as a format string where multiple data sources can be injected into a single tag.

Specification

Syntax

  1. Plain Mode (Legacy Support): If the attribute contains no braces (e.g., page.title), it behaves exactly as it does today (evaluates the fallback list and replaces the entire innerHTML).
  2. Interpolation Mode: If the attribute contains curly braces { }, the string is treated as a template.
    • Content inside { } is treated as a standard HyperTemplates variable/fallback list.
    • Content outside { } is treated as literal text.

Behavior

  • The parser identifies all braced expressions.
  • Each expression is evaluated independently using the existing fallback logic.
  • If a braced expression evaluates to an empty string (all variables in its list are missing), it renders as an empty string within the template.

Example Usage

1. Page Titles with Site Context

<!-- Input (where site.untitled_post is a constant string "Untitled Post" defined in site.yaml -->
<title ht-content="{page.title,site.untitled_post} | {site.title}">Placeholder</title>

<!-- Output (if page.title is "My Page" and site.title is "My Site") -->
<title>My Page | My Site</title>

<!-- Output (if page.title is empty, falls back to site.untitled_post for that braced expression) -->
<title>Untitled Post | My Site</title>

2. By line, with potentially missing post authors and default site author

<!-- Input -->
<span ht-content="By: {page.author,site.default_author}">By: Admin</span>

<!-- Output -->
<span>By: John Doe</span>

Benefits

  • Flexibility: Allows complex string construction without adding extra HTML elements.
  • Backwards Compatibility: Existing ht-content attributes continue to work without modification.
  • Consistency: Uses the same comma-delimited fallback logic developers already understand.

Go Implementation Proposal

The parser will use a regular expression to find all {} blocks, evaluate the inner expression via the existing variable resolver, and rebuild the string.

Example: Go implementation

package main

import (
	"fmt"
	"regexp"
	"strings"
)

// Mock resolver for the existing variable logic
func resolveVariable(expression string) string {
	// This would be your existing logic that splits by comma
	// and looks up values in your data map.
	data := map[string]string{
		"page.title": "My Page Title",
		"site.title": "HyperTemplates Blog",
	}

	parts := strings.Split(expression, ",")
	for _, p := range parts {
		val := data[strings.TrimSpace(p)]
		if val != "" {
			return val
		}
	}
	return ""
}

// InterpolateContent handles the new braced logic
func InterpolateContent(attrValue string) string {
	// Regex to find everything inside { }
	re := regexp.MustCompile(`\{([^}]+)\}`)
	
	// If no braces are found, treat the whole string as a single expression (Legacy)
	if !re.MatchString(attrValue) {
		return resolveVariable(attrValue)
	}

	// Replace all {expression} with resolved values
	return re.ReplaceAllStringFunc(attrValue, func(match string) string {
		// Strip the braces to get the expression
		expression := match[1 : len(match)-1]
		return resolveVariable(expression)
	})
}

func main() {
	template := "{page.title,site.untitled} | {site.title}"
	result := InterpolateContent(template)
	fmt.Println(result) // Output: My Page Title | HyperTemplates Blog
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions