Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Echo Golang Template Issue: Content Not Changing on Different Routes #2751

Open
alizainsoomro opened this issue Feb 24, 2025 · 0 comments
Open

Comments

@alizainsoomro
Copy link

alizainsoomro commented Feb 24, 2025

Hi,

I am using the Echo framework in Golang to serve HTML templates, but I have an issue where the title updates correctly. However, the content remains the same for both the home (/) and about (/about) pages.

When I navigate to /about, the title changes to "About | Application", but the content is still the same as the home page (/) instead of showing the correct about page content.

Project Structure

/app
  /handlers
    home.go
    about.go
  /views
    renderer.go
  /templates
    /layouts
      main.html
    /pages
      index.html
      about.html
  main.go

Code Implementation main.go (Entry Point)

package main

import (
    "app/handlers"
    "app/views"

    "github.com/labstack/echo/v4"
    "github.com/labstack/echo/v4/middleware"
)

func main() {
    e := echo.New()

    e.Use(middleware.Logger())
    e.Use(middleware.Recover())

    e.Renderer = views.NewRenderer()

    e.GET("/", handlers.Home)
    e.GET("/about", handlers.About)

    e.Static("/static", "static")

    e.Logger.Fatal(e.Start(":8080"))
}

views/renderer.go (Template Renderer)

package views

import (
    "html/template"
    "io"
    "path/filepath"
    "strings"

    "github.com/labstack/echo/v4"
)

type TemplateRenderer struct {
    templates *template.Template
}

func NewRenderer() *TemplateRenderer {
    funcMap := template.FuncMap{
        "dict": func(values ...interface{}) map[string]interface{} {
            if len(values)%2 != 0 {
                panic("invalid dict call")
            }
            dict := make(map[string]interface{}, len(values)/2)
            for i := 0; i < len(values); i += 2 {
                key, ok := values[i].(string)
                if !ok {
                    panic("dict keys must be strings")
                }
                dict[key] = values[i+1]
            }
            return dict
        },
    }

    tmpl := template.New("").Funcs(funcMap)

    layoutGlob := filepath.Join("templates", "layouts", "*.html")
    pagesGlob := filepath.Join("templates", "pages", "*.html")

    tmpl = template.Must(tmpl.ParseGlob(layoutGlob))
    tmpl = template.Must(tmpl.ParseGlob(pagesGlob))

    normalizedTemplates := template.New("").Funcs(funcMap)
    for _, t := range tmpl.Templates() {
        if t.Tree == nil || t.Tree.Root == nil {
            continue
        }
        name := t.Name()
        if strings.HasPrefix(name, "pages/") {
            name = strings.TrimPrefix(name, "pages/")
        }
        name = strings.TrimSuffix(name, ".html")
        _, err := normalizedTemplates.New(name).Parse(t.Tree.Root.String())
        if err != nil {
            panic("Failed to normalize template names: " + err.Error())
        }
    }

    return &TemplateRenderer{
        templates: normalizedTemplates,
    }
}

func (t *TemplateRenderer) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
    return t.templates.ExecuteTemplate(w, name, data)
}

handlers/home.go

package handlers

import (
    "net/http"

    "github.com/labstack/echo/v4"
)

func Home(c echo.Context) error {
    return c.Render(http.StatusOK, "index", map[string]interface{}{})
}

handlers/about.go

package handlers

import (
    "net/http"

    "github.com/labstack/echo/v4"
)

func About(c echo.Context) error {
    return c.Render(http.StatusOK, "about", map[string]interface{}{})
}

Templates

templates/layouts/main.html (Layout)

{{ define "layout" }}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ block "title" . }}{{ if .title }}{{ .title }} | Application{{ else }}Application{{ end }}{{ end }}</title>
    <link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
    <nav>
        <a href="/">Home</a> | <a href="/about">About</a>
    </nav>
    <main>
        {{ template "content" . }}
    </main>
</body>
</html>
{{ end }}

templates/pages/index.html

{{ template "layout" (dict "title" "Home") }}

{{ define "content" }}
    <h1>Welcome to the Home Page</h1>
    <p>This is a simple multi-page app with Echo.</p>
{{ end }}

templates/pages/about.html

{{ template "layout" (dict "title" "About") }}

{{ define "content" }}
    <h1>About</h1>
    <p>This is a simple multi-page app with Echo.</p>
{{ end }}

Expected Behavior / should render index.html inside layout/main.html. /about should render about.html inside layout/main.html.

Actual Behavior / works as expected. /about only updates the title but keeps the content from / (Home Page).

Possible Issue I suspect that {{ template "layout" (dict "title" "About") }} in about.html is calling layout directly and rendering "index.html" content instead of its own.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant